/* */

24 de agosto de 2007

Capítulo 9

.
Encapsulamiento de Estrucuras


Las clases permiten encapsular la implementación de la funcionalidad de los objetos. El usuario (programa que utitiliza los objetos) no necesita conocer como está programado el objeto que está utilizando. Solo necesita conocer su interface. Es decir: los métodos (procedimientos y funciones) definidos en la parte pública de la clase.

Para probar esto desarrollaremos la clase Pila. Primero implementaremos la pila en un array. Luego programaremos otra versión que implemente la pila sobre una lista enlazada.

Al programa principal no le debe interesar como está programada (por dentro) la pila. Si funciona bien entonces la usa y punto.

uPila.pas

   1:
2:
unit uPila;
3:interface
4:
5:const
6: MAX_PILA = 100;
7:
8:type
9:
10: Pila = class
11: private
12: arr:array[1..100] of integer;
13: len:integer;
14: public
15: constructor create();
16: procedure poner(v: integer);
17: function sacar(): integer;
18: end;
19:
20:implementation
21:
22:constructor Pila.create();
23:begin
24: len:=0;
25:end;
26:
27:procedure Pila.poner(v: integer);
28:begin
29: len:=len+1;
30: arr[len]:=v;
31:end;
32:
33:function Pila.sacar():integer;
34:begin
35: sacar:=arr[len];
36: len:=len-1;
37:end;
39:end.
38:

Si analizamos la sección public vemos que la clase Pila tiene un constructor y dos métodos: poner y sacar.

Las variables de instancia son arr (el array sobre el que implementamos la pila) y len (la variable que indica la cantidad de elementos que se encuentran apilados).

La variable len debe comenzar en cero (ya que al inicio la pila no tiene elementos). Esta asignación la hacemos en el constructor.

Luego, analizando la sección implementation vemos la implementación de los métodos poner y sacar.

En la implemención de poner primero incrementamos len y luego asignamos en arr[len] el elemento v que vamos a meter en la pila. En la implementación de sacar primero asignamos el elemento arr[len] como valor de retorno de la función y luego decrementamos len.


Ahora veamos un programa que pruebe la clase Pila.

testPila.pas
   1:
2:uses uPila_lista;
3:
4:var p:Pila;
5:begin
6: p:=Pila.create();
7: p.poner(1);
8: p.poner(2);
9: p.poner(3);
10:
11: writeln( p.sacar() );
12: writeln( p.sacar() );
13: writeln( p.sacar() );
14:end.
15:

Vemos que el programa principal simplemente crea una instancia de Pila y la utiliza. En el programa no nos "preocupamos" por manejar el len ni el array. Simplemente trabajamos con la pila.

Ahora analizaremos otra versión de la clase Pila, la cual implementaremos sobre una lista enlazada.

Para esto primero vamos a definir un registro Nodo y un tipo PNodo como ^Nodo. Los definiremos un una unit (untNodo.pas) separada porque los vamos a volver a utilizar más adelante, cuando analicemos la clase ListaOrdenada.

untNodo.pas
   1:
2:unit untNodo;
3:
4:interface
5:
6:type
7:
8: PNodo = ^Nodo;
9: Nodo = record
10: info: integer;
11: sig:PNodo;
12: end;
13:
14:implementation
15:
16:end.
17:

Veamos la clase Pila implementada con una lista enlazada.

uPila.pas (implementada en una lista enlazada)
   1:
2:unit uPila;
3:
4:interface
5:uses untNodo;
6:
7:type
8: Pila = class
9: private
10: p:PNodo;
11: public
12: constructor create();
13: procedure poner(v: integer);
14: function sacar(): integer;
15: end;
16:
17:implementation
18:
19:constructor Pila.create();
20:begin
21:end;
22:
23:procedure Pila.poner(v: integer);
24:var nuevo:PNodo;
25:begin
26: new(nuevo);
27: nuevo^.info:=v;
28: nuevo^.sig:=p;
29: p:=nuevo;
30:end;
31:
32:function Pila.sacar():integer;
33:var aux:PNodo;
34:begin
35: sacar:=p^.info;
36: aux:=p;
37: p:=p^.sig;
38: dispose(aux);
39:end;
40:
41:end.
42:

Como podemos ver, la parte pública es exactamente igual. No cambió respecto de la implementación anterior. Es decir que la interface de los objetos tipo Pila sigue siendo la misma por lo tanto este cambio no afecta a los programa que los utilicen..

Obviamente la implementación es totalmente diferente. A nivel variables de instancia ya no están ni arr ni len. En su lugar está el puntero p de tipo PNodo. Luego la implementación de los métodos poner y sacar es la misma que estudiamos en el capítulo 7.


Clase ListaOrdenada

Para terminar, analizaremos una clase que implementa una lista sobre la cual se podrán insertar valores ordenados en forma ascendente, con y sin repetición.


Veamos la interface de la unit uListaOrdenada.pas.

uListaOrdenada.pas
   1:
2:unit uListaOrdenada;
3:
4:interface
5:uses untNodo;
6:
7:type
8: ListaOrdenada = class
9: private
10: lst:PNodo;
11: actual:PNodo;
12: public
13: constructor create();
14:
15: // busca el valor v retorna
16: // true => esta, false => no esta
17: function buscar(v:integer):boolean;
18:
19: // inserta (ordenado) un valor
20: function insertar(v:integer
21: ; okRep:boolean): boolean;
22:
23: // libera los nodos de la lista
24: procedure liberar();
25:
26: // asigna el siguiente valor de la lista
27: // en la variable v y retorna true o false
28: // si existe o no un proximo valor
29: function siguiente(var v:integer):boolean;
30: end;
31:
32:// :
33:// mas abajo esta la seccion implementation
34:// :
35:


Ahora analizaremos la implementación de cada uno de los métodos definidos en la interface.

uListaOrdenada.pas (implementación: function buscar)
 1:
2:implementation
3:
4:function ListaOrdenada.buscar(v:integer): boolean;
5:var aux: PNodo;
6:begin
7: aux:=lst;
8: while( (aux<>NIL) AND (aux^.info<>v) ) do begin
9: aux:=aux^.sig;
10: end;
11:
12: if( aux<>NIL ) then begin
13: buscar:=true;
14: end else begin
15: buscar:=false;
16: end;
17:end;
18:

La implementación del método buscar es exactamente igual a la que estudiamos en el capítulo 7. La única diferencia es que como el puntero al inicio de la lista (lst) es una variable de instancia no es necesario recibirla como parámetro.

El puntero lst (y actual para la implementación de siguiente) se inicializan en NIL en el constructor.


uListaOrdenada.pas (implementación: constructor create)
18:
19:constructor ListaOrdenada.create();
20:begin
21: lst:=NIL;
22: actual:=NIL;
23:end;
24:


uListaOrdenada.pas
(implementación: function insertar)

El objetivo de esta función es insertar (ordenado) un valor en la lista. Si el valor ya existe entonces lo volvemos a insertar o no dependiendo del valor de okRep.

La implementación es prácticamente igual a la estudiada en el capítulo 7.
24:
25:function ListaOrdenada.insertar(
26: v:integer
27: ; okRep: boolean): boolean;
28:var aux,ant,nuevo: PNodo;
29:begin
30: // la lista esta vacia...
31: if( lst = NIL ) then begin
32: new(nuevo);
33: nuevo^.info:=v;
34: nuevo^.sig:=NIL;
35: lst:=nuevo;
36: insertar:=false;
37: exit;
38: end;
39:
40: // el valor es anterior al primero
41: if( v < lst^.info ) then begin
42: new(nuevo);
43: nuevo^.info:=v;
44: nuevo^.sig:=lst;
45: lst:=nuevo;
46: insertar:=false;
47: exit;
48: end;
49:
50: aux:=lst;
51: ant:=NIL;
52:
53: while((aux<>NIL) AND (aux^.info<=v)) do begin
54: ant:=aux;
55: aux:=aux^.sig;
56: end;
57:
58: // llego al final de la lista
59: if( aux = NIL ) then begin
60: new(nuevo);
61: nuevo^.info:=v;
62: ant^.sig:=nuevo;
63: nuevo^.sig:=NIL;
64: insertar:=false;
65: exit;
66: end;
67:
68: // el valor existe...
69: if( aux^.info = v ) then begin
70: if( okRep ) then begin
71: new(nuevo);
72: nuevo^.info:=v;
73: nuevo^.sig:=aux^.sig;
74: aux^.sig:=nuevo;
75: end;
76: // estaba... retorno true
77: insertar:=true;
78: exit;
79: end;
80:
81: // encontre uno mayor...
82: // insertamos entre ant y aux
83: new(nuevo);
84: nuevo^.info:=v;
85: nuevo^.sig:=aux;
86: ant^.sig:=nuevo;
87: insertar:=false;
88:end;
89:


uListaOrdenada.pas
(implementación: function siguiente)
 89:
90:function ListaOrdenada.siguiente(
91: var v:integer):boolean;
92:begin
93: if( actual=NIL ) then begin
94: actual:=lst;
95: v:=actual^.info;
96: siguiente:=true;
97: end else begin
98: actual:=actual^.sig;
99: if( actual<>NIL ) then begin
100: v:=actual^.info;
101: siguiente:=true;
102: end else begin
103: siguiente:=false;
104: end;
105: end;
106:end;
107:

Esta función es "nueva". Permite iterarar la lista. La primera vez que la invocamos retorna (en el parámetro v) el valor del primer nodo. La segunda vez retorna el valor del segundo nodo y así hasta el último nodo.

La función retorna true o false según exista un nodo más para iterar por lo tanto en un programa podremos iterar la lista (recorrerla de principio a fin) así:

while( lista.siguiente(v) ) do begin ....


uListaOrdenada.pas (implementación: procedure liberar)
107:
108:procedure ListaOrdenada.liberar();
109:var auxSig,aux: PNodo;
110:begin
111: aux:=lst;
112: while( aux<>NIL ) do begin
113: auxSig:=aux^.sig;
114: dispose(aux);
115: aux:=auxSig;
116: end;
117:end;
118:
119:end.
120:

otra vez, la misma implementación que estudiamos antes.


Ahora veamos un programa principal que utiliza un objeto de la clase ListaOrdenada. Pide al usuario que ingrese valores y los agrega a la lista. Cuando el usuario finaliza el ingreso de datos (ingresando un valor cero) itera la lista para mostrar su contenido y luego la libera.

testListaOrdenada.pas
   1:
2:uses uListaOrdenada;
3:var
4: lista:ListaOrdenada;
5: valor, iOkRep: integer;
6: okRep:boolean;
7:begin
8:
9: lista:=ListaOrdenada.create();
10:
11: write(
12: 'Ingrese [valor, repetido (1=>si, 0=>no)]: ');
13: readln(valor,iOkRep);
14:
15: while( valor<>0 ) do begin
16:
17: if( iOkRep>0 ) then begin
18: okRep:=true;
19: end else begin
20: okRep:=false;
21: end;
22:
23: lista.insertar(valor,okRep);
24:
25: write(
26: 'Ingrese [valor, repetido (1=>si, 0=>no)]: ');
27:
28: readln(valor,iOkRep);
29: end;
30:
31: writeln('---[LISTA]----');
32:
33: // iteramos la lista con el metodo siguiente()
34: while( lista.siguiente(valor) ) do begin
35: writeln(valor);
36: end;
37:
38: lista.liberar();
39:end.
40:







Algoritmos y Estructuras de Datos UTN - UBA.

No hay comentarios: