Está en la página 1de 15

Estructuras anidadas

La conveniencia de coger nombres de funciones y datos fuera del espacio de nombre


global es aplicable a las estructuras. Puede anidar una estructura dentro de otra
estructura, y por tanto guardar juntos elementos asociados. La sintaxis de declaración es
la que podría esperarse, tal como puede ver en la siguiente estructura, que implementa
una pila como una lista enlazada simple de modo que «nunca» se queda sin memoria.

//: C04:Stack.h
// Nested struct in linked list
#ifndef STACK_H
#define STACK_H

struct Stack {
struct Link {
void* data;
Link* next;
void initialize(void* dat, Link* nxt);
}* head;
void initialize();
void push(void* dat);
void* peek();
void* pop();
void cleanup();
};
#endif // STACK_H ///:~

Listado 4.9. C04/Stack.h

La struck anidada se llama Link, y contiene un puntero al siguiente Link en la lista y


un puntero al dato almacenado en el Link. Si el siguiente puntero es cero, significa que
es el último elemento de la lista.

Fíjese que el puntero head está definido a la derecha después de la declaración de la


struct Link, es lugar de una definición separada Link* head. Se trata de una sintaxis
que viene de C, pero que hace hincapié en la importancia del punto y coma después de
la declaración de la estructura; el punto y coma indica el fin de una lista de definiciones
separadas por comas de este tipo de estructura (Normalmente la lista está vacía.)

La estructura anidada tiene su propia función initialize(), como todas las estructuras
hasta el momento, para asegurar una inicialización adecuada. Stack tiene tanto función
initialice() como cleanup(), además de push(), que toma un puntero a los datos
que se desean almacenar (asume que ha sido alojado en el montículo), y pop(), que
devuelve el puntero data de la cima de la Stack y elimina el elemento de la cima. (El
que hace pop() de un elemento se convierte en responsable de la destrucción del objeto
apuntado por data.) La función peak() también devuelve un puntero data a la cima de
la pila, pero deja el elemento en la Stack.

Aquí se muestran las definiciones de los métodos:

//: C04:Stack.cpp {O}


// Linked list with nesting
#include "Stack.h"
#include "../require.h"
using namespace std;

void
Stack::Link::initialize(void* dat, Link* nxt) {
data = dat;
next = nxt;
}

void Stack::initialize() { head = 0; }

void Stack::push(void* dat) {


Link* newLink = new Link;
newLink->initialize(dat, head);
head = newLink;
}

void* Stack::peek() {
require(head != 0, "Stack empty");
return head->data;
}

void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}

void Stack::cleanup() {
require(head == 0, "Stack not empty");
} ///:~

Listado 4.10. C04/Stack.cpp

La primera definición es particularmente interesante porque muestra cómo se define un


miembro de una estructura anidada. Simplemente se usa un nivel adicional de
resolución de ámbito para especificar el nombre de la struct interna.
Stack::Link::initialize() toma dos argumentos y los asigna a sus atributos.

Stack::initialize() asgina cero a head, de modo que el objeto sabe que tiene una
lista vacía.

Stack::push() toma el argumento, que es un puntero a la variable a la que se quiere


seguir la pista, y la apila en la Stack. Primero, usa new para pedir alojamiento para el
Link que se insertará en la cima. Entonces llama a la función initialize() para
asignar los valores apropiados a los miembres del Link. Fijese que el siguiente puntero
se asigna al head actual; entonces head se asigna al nuevo puntero Link. Esto apila
eficazmente el Link en la cima de la lista.
Stack::pop() captura el puntero data en la cima actual de la Stack; entonces mueve
el puntero head hacia abajo y borra la anterior cima de la Stack, finalmente devuelve el
puntero capturado. Cuando pop() elemina el último elemento, head vuelve a ser cero,
indicando que la Stack está vacía.

Stack::cleanup() realmente no hace ninguna limpieza. En su lugar, establece una


política firme que dice «el programador cliente que use este objeto Stack es
responsable de des-apilar todos los elementos y borrarlos». require() se usa para
indicar que ha ocurrido un error de programación si la Stack no está vacía.

¿Por qué no puede el destructor de Stack responsabilizarse de todos los objetos que el
programador cliente no des-apiló? El problema es que la Stack está usando punteros
void, y tal como se verá en el Capítulo 13 usar delete para un void* no libera
correctamente. El asunto de «quién es el responsable de la memoria» no siempre es
sencillo, tal como veremos en próximos capítulos.

Un ejemplo para probar la Stack:

//: C04:StackTest.cpp
//{L} Stack
//{T} StackTest.cpp
// Test of nested linked list
#include "Stack.h"
#include "../require.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[]) {


requireArgs(argc, 1); // File name is argument
ifstream in(argv[1]);
assure(in, argv[1]);
Stack textlines;
textlines.initialize();
string line;
// Read file and store lines in the Stack:
while(getline(in, line))
textlines.push(new string(line));
// Pop the lines from the Stack and print them:
string* s;
while((s = (string*)textlines.pop()) != 0) {
cout << *s << endl;
delete s;
}
textlines.cleanup();
} ///:~

Listado 4.11. C04/StackTest.cpp

Es similar al ejemplo anterior, pero en este se apilan líneas de un fichero (como


punteros a cadena) en la Stack y después los des-apila, lo que provoca que el fichero
sea imprimido en orden inverso. Fíjese que pop() devuelve un void* que debe ser
moldeado a string* antes de poderse usar. Para imprimir una cadena, el puntero es
dereferenciado.

Como textlines se llena, el contenido de line se «clona» para cada push() creando
un new string(line). El valor devuelto por la expresión new es un puntero al nuevo
string que fue creado y al que se ha copiado la información de la line. Si se hubiera
pasado directamente la dirección de line a push(), la Stack se llenaría con direcciones
idénticas, todas apuntando a line. Más adelante en ese libro aprenderá más sobre este
proceso de «clonación».

El nombre del fichero se toma de línea de comando. Para garantizar que hay suficientes
argumentos en la línea de comando, se usa una segunda función del fichero de cabecera
require.h: requireArgs() que compara argc con el número de argumentos deseado e
imprime un mensaje de error y termina el programa si no hay suficientes argumentos.

4.8.1. Resolución de ámbito global

El operador de resolución de ámbito puede ayudar en situaciones en las que el nombre


elegido por el compilador (el nombre «más cercano») no es el que se quiere. Por
ejemplo, suponga que tiene una estructura con un identificador local a, y quiere
seleccionar un identificador global a desde dentro de un método. El compilador, por
defecto, elegirá el local, de modo que es necesario decirle que haga otra cosa. Cuando se
quiere especificar un nombre global usando la resolución de ámbito, debe usar el
operador sin poner nada delante de él. A continuación aparece un ejemplo que muestra
la resolución de ámbito global tanto para una variable como para una función:

//: C04:Scoperes.cpp
// Global scope resolution
int a;
void f() {}

struct S {
int a;
void f();
};

void S::f() {
::f(); // Would be recursive otherwise!
::a++; // Select the global a
a--; // The a at struct scope
}
int main() { S s; f(); } ///:~

Listado 4.12. C04/Scoperes.cpp

Sin resolución de ámbito en S::f(), el compilador elegiría por defecto las versiones
miembro para f() y a.

Estructuras Anidadas
Estructuras de decisión anidadas (en escalera)
 
Las estructuras de selección Si-entonces y si-entonces-si_no implican la
selección de dos alternativas.
Es posible también utilizar la instrucción si para diseñar estructuras de
selección que contengan más de dos alternativas. Una sentencia si-entonces
puede contener otra estructura si-entonces, y esta a su vez puede contener
otra, y así sucesivamente; al mismo tiempo, dentro de cada estructura pueden
existir diferentes acciones.
Las estructuras si interiores a otras estructuras si se denominan anidadas.
 
Si  <condición> entonces
                Si < condición2> entonces
                               Si < condición3> entonces
                                               .
                                               .
                                               .
                                               < acciones>
                               Fin_si
                Fin_si
Fin_si

Anidamiento (informática)
De Wikipedia, la enciclopedia libre
Saltar a: navegación, búsqueda

El anidamiento (llamado nesting en inglés) es la práctica de incorporar llamadas


(calls) a funciones o procedimientos (unas) dentro de otras, mediante la inclusión de
diversos niveles de paréntesis.

Debido a que la potencial acumulación de éstos últimos suele hacer que la edición y la
detección de errores se vuelva un proceso engorroso, los entornos de programación
modernos -así como los programas de planilla de cálculo- resaltan en negrita el par
correspondiente a la posición que está editando el programador o usuario en cada
momento. El control (automático) del balance o equilibrio entre los paréntesis de
apertura y de cierre se suele conocer como brace match checking en inglés.

Naturalmente, para la resolución matemática de estas complejas formulas encadenadas,


las expresiones deben ser evaluadas desde adentro hacia afuera, ya que los resultados
de las más internas sirven, temporalmente, de datos de entrada de las exteriores.

En las planillas de cálculo[editar]


En las hojas de cálculo, se suelen anidar o agrupar funciones unas dentro de otras,
derivando en fórmulas relativamente complejas. El programa OpenOffice.org Calc
permite, mediante su asistente de funciones (function wizard), navegar a través de los
varios o múltiples niveles de anidamiento, permitiendo editar (y eventualmente corregir)
cada una de ellas por separado. Tal vez de manera sorprendente, su rival Microsoft
Excel no posee esa característica, eventualmente deseable cuando se trabaja en algunas
grandes planillas.

En programación[editar]
En los lenguajes de programación estructurada, el anidamiento está relacionado a la
inclusión de estructuras de control dentro de otras, usualmente indicado mediante la
inclusión de distintos niveles de sangría (llamada indentation en inglés) dentro del
código fuente, como se muestra en el sencillo código BASIC siguiente:

function BuscarCodigo(cod as string) as integer


dim linea, ruta as string
dim valor_a_devolver as integer

ruta="C:\Probar.csv"
if FileExists(ruta) then
open "C:\Probar.csv" for input as #1
do while not EOF(1)
line input #1, linea
if left(linea, 3)=cod then
'Realizar una acción o varias acciones
End if
loop
close #1
BuscarCodigo=valor_a_devolver
end function

En este simple ejemplo, la estructura condicional if... then... end if ("si... entonces... fin
si") está anidada dentro de otra que la contiene, el ciclo do while... loop ("repetir...
mientras", literalmente "hacer mientras... bucle").

Ups, perdón por este pequeño receso, pero ya estoy listo para la siguiente
lección vamos a hablar más de las instrucciones condicionales, específicamente
de instrucciones de condición anidadas.

Una instrucción de condición anidada es cuando una instrucción condicional


tiene en su rama del verdadero o falso otra instrucción condicional. No todo se
puede arreglar con un solo if, existen una gran infinidad de programas que
requieren de estructuras anidadas. Veamos algunos ejemplos de estos tipos de
problemas, solo se muestra el código de la parte del if en la solución de los
ejemplos.

Ejemplo 1

Realizar un programa que lea un número y que indique si es positivo y además


mayor que 10.
Solución

Con una comparación con 0 descubrimos si es positivo o no, si lo es, basta con
agregar otro if para compararlo con 10 e indicar si es positivo y mayor que 10
este es el código:
if ( Numero >= 0 ) {
if (Numero > 10) {
stringItem.setText("El Número es positivo y mayor a 10");
}
else {
stringItem.setText("El Número es positivo y menor a 10");
}
}
else
stringItem.setText("El Número es negativo");

En el ejemplo se puede observar claramente como en la rama del verdadero del


primer if se agrega otro if, lo anterior es lo que conocemos como if anidados, un
if dentro de otro if.

Algo importante es la sangría (el espacio que se deja al iniciar las sentencias), se
utiliza para ir agrupando claramente los if, y no confundirse, es altamente
recomendable agregar la sangría a los if.

Ejemplo #2

Realizar un programa para ingresar la longitud de tres lados de un triangulo (L1,


L2 y L3) y que el programa indique si el triangulo leído es equilátero (los tres
lados iguales), o si es isósceles (solo dos lados son iguales) y finalmente que
indique si es escaleno (los tres lados diferentes).

Solución

Otra vez se requiere varias comparaciones, varios if anidados, por ejemplo para
saber si el triangulo es equilátero, se comparan los lados para saber si son
iguales, como se observa en el código siguiente:
if ( L1 == L2 ) {
if ( L2 == L3)
stringItem.setText("El triangulo es equilatero");
}
Para saber si es el triangulo es isósceles se requieren varias comparaciones para
comprobar las tres opciones posibles, para saber si hay dos lados iguales de la
siguiente forma:
if ( L1 == L2 ) { //Opción 1: cuando L1 es igual a L2 pero diferente a
L3
if ( L3 != L1)
stringItem.setText("El triangulo es isósceles");
}
else {
if ( L1 == L3 ) { //Opción 2: cuando L1 es igual a L3 pero diferente a
L2
if ( L2 != L1)
stringItem.setText("El triangulo es isósceles");
}
else {
if ( L3 == L2 ) {//Opción 3: cuando L3 es igual a L2 pero diferente a
L1
if ( L1 != L2)
stringItem.setText("El triangulo es isósceles");
}
}
}
No lo había mencionado en el blog pero // en Java significa un comentario y se
agrega para explicar el código, hay que tener cuidado con las { } por eso
precisamente se recomienda ampliamente el uso de sangría para identificar
claramente los grupos de instrucciones que van en el if o en el else y cerrar
adecuadamente las llaves.

Finalmente para saber si el triangulo es escaleno pues se compararan los lados


para saber si son diferentes algo como el caso del triangulo equilátero, este es el
código.
if ( L1 != L2 ) {
if ( L2 != L3)
if ( L1 != L3)
stringItem.setText("El triangulo es escaleno");
}
La animación siguiente muestra el código completo para este ejemplo, además
se observa la ejecución del programa como la hace el procesador línea por línea
para que el lector vea los caminos que se siguen con las diferentes
combinaciones que hay.
Basta con introducir los valores de los lados del triangulo en los campos de texto
y dar clic en el botón de iniciar la animación, al hacerlo aparece una línea azul
que indica las comparaciones que hace el procesador hasta encontrar la
solución.
Operadores Lógicos

Afortunadamente en java existen los llamados operadores lógicos que me


permiten cambiar los if’s anidados en un solo if. Los operadores lógicos que
veremos son los siguientes:

Operador AND ( Y )

La sintaxis del operador es así: &&

Su tabla de verdad nos muestra cuando dos variables unidas por un AND son
verdaderas o falsas:
Operador 1 Operador 2 Operador 1 AND Operador 2
F F F
F V F
V F F
V V V

Véase la tabla de verdad, solo hay una opción para que el operador AND sea
verdadero: cuando sus dos operadores sean verdaderos.

Con este operador se puede saber si el triangulo es equilátero con un solo if así:
if (( L1 == L2 ) && ( L2 == L3)) {
stringItem.setText("El triangulo es equilátero");
}
Haciendo una analogía con la tabla de verdad, el operador 1 es (L1 == L2), el
operador AND es && y el operador 2 es (L2 == L3) así que, podríamos decir con
el if que “Si L1 es igual que L2 y L2 igual que L3 entonces el triangulo es
equilátero”.

Recuerde la sintaxis el operador AND es && y siempre debe haber ( ) que


encierran toda la expresión y de preferencia cada operador por ejemplo (L1 ==
L2) también hay que encerrarlo entre ( ).

Para saber si el triangulo es escaleno también se pude usar este operador de la


siguiente forma:

if (( L1 != L2 ) && ( L2 != L3) && ( L1 != L3 )) {


stringItem.setText("El triangulo es escaleno");
}
¡¡Que tal, tres if’s los cambiamos por uno solo!!

El operador AND funciona con los operadores que sean, siempre recordando
que para que la expresión sea verdadera a fuerzas cada operando tiene que ser
verdadero.

Operador OR ( O )

La sintaxis del operador es así: ||

Su tabla de verdad nos muestra cuando dos variables unidas por un OR son
verdaderas o falsas:

Operador 1 Operador 2 Operador 1 OR Operador 2


F F F
F V V
V F V
V V V

Con este operador basta con que un operador sea verdadero para que la
expresión total sea verdadera.
Por ejemplo para saber si el triangulo es isósceles, se tienen tres opciones que
podemos agrupar con el operador AND.

Opción 1: cuando L1 y L2 son iguales pero L3 es diferente

if (( L1 == L2 ) && ( L3 != L1)) {
}

Opción 2: cuando L1 y L3 son iguales pero L2 es diferente

if (( L1 == L3 ) && ( L2 != L1)) {

Opción 3: cuando L2 y L3 son iguales pero L1 es diferente

if (( L2 == L3 ) && ( L1 != L2)) {

}
Ahora bien las tres opciones se pueden agrupar con un OR, porque basta con
que una de las opciones sea verdadera para que el triangulo sea isósceles, el
código final para saber si el triangulo es isósceles queda con un solo if así:
if ((( L1 == L2 ) && ( L3 != L1)) || (( L1 == L3 )
&& ( L2 != L1)) || (( L2 == L3 ) && ( L1 != L2))) {

stringItem.setText("El triangulo es isósceles");


}
En conclusión, los operadores lógicos pueden facilitarnos el trabajo cuando se
trata de muchas instrucciones condicionales y más si son anidadas, como se vio
en este ejemplo del triangulo, que se realizaba con más de 10 if’s y con los
operadores lógicos se redujo a solamente 3. Hasta aquí vamos a dejar este post,
recuerda practicar es la clave del éxito.

CONDICIONES ANIDADAS
•          Los estatutos if implementan decisiones que implican una o dos alternativas, un
estatuto if es anidada cuando la sentencia de la rama verdadera o la rama falsa es a su
vez una sentencia if.

•          Un estatuto if anidada se puede utilizar para implementar decisiones con varias
alternativas o multi-alternativas

Sintaxis:

          if (condición1)

                  estatuto1

             else

                  if (condición2)

                        estatuto2

                                            •

                                            •

                                            •

                           else

                       if (condiciónn)

                             estatuton

else
                                  

                              estatutoe

Si tenemos el siguiente fragmento de código:

      if (condición 1)
      if (condición 2)
              estatuto;
      else               // este else pertenece al if de la condición 2, pues se

                                // se asocia al if más cercano       


           estatuto;

Si queremos que el else pertenezca al primer if debemos poner:

      if (condición 1)
      {
         if (condición 2)
               estatuto;
      }
      else               // con el uso de llaves cerramos el if anidado y el else
         estatuto;     // pertenece al primer if

Ejemplo:

   Determinar si un número es cero, positivo o negativo.

      if (num == 0)
       cout << " El número es cero";
   else
       if (num > 0)
           cout << " El número es positivo";
       else
           cout << " El número es negativo";

Ejemplo I: Programa que lee 3 números enteros diferentes y los despliega de mayor a
menor.

#include <iostream.h>

/* Declaración de variables globales */

       int a,b,c;

int main()

    cout << "Dame los 3 numeros "<< endl;

    cin >> a>> b >> c;

    if (a > b)

       if ( b > c)

           cout <<endl<< a << " " << b << " " << c <<endl;

       else

        if (c > a)

            cout <<endl<< c<< " " << a << " " << b<< endl;
        else

           cout <<endl<< a << " " << c << " " << b <<endl; 

      

    if ( a > c) { 

              if ( b > a)

                 cout <<endl<< b<< " " << a << " " << c <<endl;

    }

    else

     if ( b > c )

       cout <<endl<< b << " " << c << " " << a <<endl;

     else

      if (c > b) {

        if (b > a)

          cout <<endl<< c << " " << b << " " << a <<endl;

      }

    system ("pause");

    return 0;

CICLOS ANIDADOS
Consiste en usar un ciclo dentro de otro (vamos a usar hasta dos anidamientos). Se puede
anidar de la siguiente forma:
1.       Un mientras dentro de un mientras:

I=1
Mientras i <= N haga
       J = 1
Mientras J <= N haga
       Bloque de instrucciones
       Fin mientras
       Bloque de instrucciones
Fin mientras

2.       Un para dentro de un para:


Para i = 1, N, 1 haga
     Para J = 1, N, 1 haga
           Bloque de instrucciones
     Fin para
Fin para

3.       Ciclos combinados:

Mientras y para.
Para y mientras.

Ejemplo: elabore un algoritmo que genere las tablas de multiplicar.

Ejemplo: en la especialidad de sistemas hay un numero conocido de estudiantes, si


ademas se conocen las n notas del periodo, elabora un algoritmo que halle la nota
definitiva de especialidad por estudiante

También podría gustarte