[ Foro de Pascal ]

Punteros. (Continuación de "duda con funciones y matrices").

11-Dec-2010 18:38
Antonio P.G.
1 Respuestas

Hola Jesús.

(Toca leer un poco... Ja ja ja).

A ver, voy a hablarte desde mi punto de vista como el que ha programado listas, pilas, colas, bicolas, árboles, y listas bidimensionales; al final el truco está en saber dónde apuntas. Si dices que tienes que hacerlo por punteros, te daré algún consejo y alguna noción.

Por cierto, antes que nada: por facilidad, sin duda yo escogería la opción de sobredimensionar usando grandes arrays, que aunque suele ocupar más memoria, la complejidad algorítmica es baja.

Otra cosa: si utilizas punteros tendrás que modificar tus métodos, que se basan en arrays.

Consejo: hoja de papel/pizarra y un par de colores para pintar flechas y celdas de memoria (para imaginar todo mucho mejor).

Lo mejor para esto es crearse una unidad que contenga el tipo de dato. A esto, si no me equivoco, se le llama TAD (Tipo Abstracto de Datos). Se trata, al fin y al cabo, de abstraerse del tipo que vas a aplicar, y utilizar funciones y procedimientos sobre éste de forma que no tengamos que hurgar dentro del tipo. Para darte una idea de esto, es como si hicieses un tipo record y luego en tu programa no accedieses a sus campos directamente, sino que lo hicieses a través de funciones. Si no queda esto muy claro, me lo dices y luego te pongo un ejemplo, que a veces se ve mejor.

Vuelvo con las listas.

Te pongo un ejemplo de una lista sencilla de lo que sería de hecho una lista de la compra. El profesor Nacho tiene esto muy bien explicado, por cierto. Allá voy:
-----------------------------------------------------
Unit UPRO;

interface

type
 PProducto = ^TProducto;  { Puntero "TProducto". }
 TProducto = record      { Tipo "Producto". }
   nombre   :  string; { Nombre del producto. }
   cantidad : integer;  { Cantidad que compraré del producto. }
   siguiente: PProducto;  { Siguiente producto que compraré. }
 end;  { Fin de definición de "TProducto". }

implementation
end.
-----------------------------------------------------
¿Por qué "PProducto" antes que "TProducto"? Porque "PProducto" es un tipo que se usa dentro de "TProducto". Supongo que cosas del Pascal...

Lo que yo tendré no será una lista de la compra; tendré un producto que comprar que puede que me lleve a otro producto. Es como el carrete de una película: tengo una diapositiva, que puede que me lleve a otra. Si hay otra, la veo. Si no hay otra después de la que estoy viendo, se acaba la película (o en el caso de la programación, te vas al valor "nil", que es algo así como el limbo...).

Añado algo más, que sería un ejemplo de programa:
-----------------------------------------------------
program LaCompra;
uses
 UPRO;
var { Variables locales (NO GLOBALES) del programa principal. }
 mi_producto1,               { Un producto. }
 mi_producto2,               { Otro producto. }
 aux          : PProducto;   { Variable auxiliar. }
                             { "aux" será como el "visor de la diapositiva de la película". }

begin
 new (mi_producto1); { Reservo memoria para el primer producto. }
 mi_producto1^.cantidad:= 6;
 mi_producto1^.nombre:= 'Computadores';
 mi_producto1^.siguiente:= nil; { De momento no tengo que comprar más cosas. }
 new (mi_producto2); { Reservo memoria para un 2º producto que quiero adquirir. }
 mi_producto2^.cantidad:= 3;
 mi_producto2^.nombre:= 'Mesas';
 mi_producto2^.siguiente:= nil;
 mi_producto1^.siguiente:= mi_producto2; { Después de los computadores van las mesas. }
 writeln (mi_producto1^.cantidad,' ',mi_producto1^.nombre); { Escribirá "6 Computadores" en pantalla. }
 writeln (mi_producto1^.siguiente^.nombre); { Escribirá "Mesas" en pantalla. }
 new (aux);  { Reservamos memoria para "aux". Si no, producirá error. }
 aux:= mi_producto1;  { Ahora aux es un puntero que apunta a la misma celda de memoria que "mi_producto1". }
 writeln (aux^.nombre); { Escribirá "Computadores" en pantalla. }
 aux:= aux^.siguiente; { Ahora "aux" apunta al siguiente de lo que estaba apuntando. }
 dispose (mi_producto1); { No lo uso más; libero la memoria que ocupaba, es decir, ya no me quedan punteros apuntando a la celda de memoria que almacenaba los datos del primer producto, y por tanto no los podré encontrar, pero me da igual.}
 writeln (aux^.nombre);  { Escribirá "Mesas" en pantalla. }
 { Me he cansado de jugar... :-) }
 dispose (aux); { Libero la memoria de los datos del segundo producto. }
end.
-----------------------------------------------------
Si te das cuenta al final no he hecho "dispose (mi_producto2);". Esto es porque "mi_producto2" apuntaba a la misma celda de memoria que "aux", por lo que sólo libero la memoria una vez. De hecho, si la intentas liberar una segunda vez te dará error, como diciendo "¡Pero si no hay nada que liberar aquí, amigo!".

Si mal no recuerdo al menos hasta aquí se podía encontrar esto en el curso de esta web. Ahora, no recuerdo acerca de las otras webs del profe.

¿Qué sucede ahora? Pues que para no tener que estar en nuestro programa accediendo de forma constante a nuestros campos del Producto, podemos crear procedimientos o funciones del tipo:
-----------------------------------------------------
procedure DefineProducto (var un_producto : PProducto; un_nombre : string; una_cantidad : integer);
 { Define el nombre y la cantidad de un producto. No direcciona a dónde a punta. }
 { Precondición debe de existir en memoria. }
 begin
   un_producto^.nombre:= un_nombre;
   un_producto^.cantidad:= una_cantidad;
 end;

procedure NuevoProducto (var un_producto : PProducto; un_nombre : string; una_cantidad : integer);
 { Crea un producto y lo define con nombre y cantidad, pero de forma aislada, sin vincular a otro. }
 begin
   new (un_producto);
   DefineProducto (un_producto,un_nombre,una_cantidad);
   un_producto^.siguiente:= nil;
 end;
-----------------------------------------------------
Así, sustituyendo instrucciones del programa por procedimientos, quedaría "más bonito", estructurado y modulado.

Como me ha llevado mucho escribir esto (espero que no haya sido en vano :-P), si hasta aquí todo va bien, o si tienes dudas, dilo, ¿ok?

Cuando tenga un poco de más tiempo continuaré.

¡Hasta luego!


13-Dec-2010 15:28
Jesús Rodríguez

WOW!!!!! que buena explicación! Eres increible Antonio me has dejado en claro muchisimas cosas, muchisimas gracias por ese tiempo tan valioso que dedicaste para explicarme todo eso, y por compartir esos conocimientos! He estado corto de tiempo y por eso no lo habia leido! Cuando tenga algo de tiempo lo probare en el compilador y estudiare a fondo, en una vista general comprendi muchas de las cosas en las cuales tenia dudas " writeln (mi_producto1^.siguiente^.nombre); " por ejemplo no sabia como imprimir a quien apuntaba el objeto, ese ejercicio de ejemplo me lo dejo en claro, Era justo lo que buscaba un ejercicio donde aparte de hacer el TAD se implemente,Agradecido enternamente Antonio. Y ten por seguro que en cuanto empiezen a surgir dudas las posteare. jejeje

Saludos!






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)