Fecha y hora actual: Domingo 25 Ago 2019 05:14
Índice del Foro

Foros de programación informática, diseño gráfico y Web

En esta comunidad intentaremos dar soporte de programación a todos los niveles, desde principiantes a profesionales de la informática, desarrollo de programas, programación web y mucho más.

Programando desde 0: 40- Introducción a los TADs.

Responder al Tema

Índice del Foro > Programación en general > Programando desde 0: 40- Introducción a los TADs.

Autor Mensaje
Kyshuo Ayame
Moderador Global


Registrado: 07 Ene 2011
Mensajes: 1043

Mensaje Publicado: Lunes 12 Dic 2011 20:07

Título del mensaje: Programando desde 0: 40- Introducción a los TADs.

Responder citando

Introducción a los Tipos Abstractos de Datos (TADs):

Bien, hemos terminado con los teóricos engorrosos, soy consciente de que recursividad es un tema pesado y que es probable que les haya quitado las ganas de seguir adelante, pero no hay otra manera de aprender a programar poderosamente si no aprenden justamente a usar las herramientas poderosas que existen hasta el momento. Asumiré entonces que ya han comprendido como funciona la recursividad.

Comenzaremos entonces a ver cómo es esto de dividir el programa en varios módulos. Como dije por allá en los comienzos de esta parte del curso, nuestros programas en Modula tendrán obligatoriamente un único módulo principal (que hasta ahora es lo que ustedes saben hacer) y luego pueden tener cero o más módulos. Estos módulos corresponden a tipos que nosotros definiremos, más concretamente, corresponden a un Tipo Abstracto de Datos. Existirán sin embargo muchos módulos que no serán TADs ya que sus tareas serán de gestión o de control sobre los TADs, pero bueno, veremos eso en su momento.
-------------------------------------------------------------------------------------

¿Qué es un Tipo Abstracto de Datos?

Conocidos también por su abreviación TAD o ADT (Abstract Data Type), los tipos abstractos de datos corresponden a una forma de abstraer una entidad real para representarla en un lenguaje de programación. Por ejemplo, se nos pide modelar un sistema que permita trabajar con altas y bajas de personas. Modula no dispone de un tipo de datos llamado Persona o alago parecido, tiene CARDINAL, INTEGER, y otros, pero nada parecido a una Persona.

¿Qué es una persona? Bueno, eso dependerá del problema planteado. Siguiendo con el ejemplo, nos podrían decir que una persona está dada por un nombre, una edad, un sueldo por hora y una cantidad de horas trabajadas. Entonces para nuestro sistema eso será una persona, un objeto que tendrá estos datos:

  • Nombre
  • Edad
  • Sueldo por hora
  • Horas trabajadas


Lo que estamos haciendo es abstraer lo que es una persona y amoldarla a lo que nos interesa de nuestro sistema ¿si? Los datos antes mencionados corresponden a los atributos de una persona. De este modo comenzamos a definirnos el Tipo Abstracto de Datos Persona, es decir, estamos creando un nuevo tipo.

Tal vez ustedes dirán que podemos hacer eso con un simple registro así:

Código:
Persona= RECORD
        Nombre: ARRAY[1..MAX_LARGO_NOMBRE] OF CHAR;
        Edad: CARDINAL;
        SueldoPorHora: REAL;
        HorasTrabajadas: REAL;
END;


La constante MAX_LARGO_NOMBRE debe estar definida antes que el tipo, eso ya lo saben.

Bien, he creado un tipo Persona con los atributos mencionados, pero esto no corresponde a un TAD porque está muy limitado. Un tipo abstracto de datos, además de definir los atributos, define operaciones que se pueden realizar sobre objetos de ese tipo. Como ven, ya comienzo a utilizar la palabra objeto, ya que estamos comenzando a acercarnos a la Programación Orientada a Objetos.

Entonces, dada una persona con esos atributos, puede resultar interesante calcular el sueldo ganado, el cual sería multiplicar lo que gana por hora por la cantidad de horas trabajadas. Entonces deberíamos proveer de una operación CalcularSueldoGanado, que, dada una persona, retorne el valor del sueldo que ha ganado.

Entonces, comenzaremos a armar un sistema pequeño que tendrá un módulo principal llamado Aplicación, el cual en principio tendrá su código sin instrucciones, así:

Creen ustedes mismos ese módulo principal y agréguenlo al un proyecto llamado Aplicacion. Ahora crearemos un segundo módulo llamado Persona para definir nuestro TAD. Recuerden que les dije que, excepto el módulo principal, los demás módulos tienen un archivo de definición y otro de implementación.

Antes que nada entonces, dejemos bien claro cómo será nuestro tipo Persona en principio, luego lo iremos ampliando hasta que quede completo. La idea es ir de a poco para que ustedes entiendan qué es lo que estamos haciendo y por qué.

Entonces, nuestro tipo Persona tendrá los atributos ya mencionados y además una operación llamada CalcularSueldoGanado que nos permitirá, dada una Persona, obtener su sueldo.

Entonces, teniendo ya nuestro proyecto creado con su módulo principal, crearemos el módulo de definición de nuestro tipo Persona:

  • Tenemos creado nuestro módulo principal, lo hemos guardado en la carpeta SRC de nuestro proyecto, lo añadimos al proyecto y actualizamos la lista de archivos. Eso está explicado al inicio de esta parte del curso.
  • Ahora crearemos un nuevo archivo mediante File → New o mediante el botón New de la barra de herramientas.




Ahora escribimos en el nuevo archivo el texto que pueden observar en la imagen. Los archivos de definición siempre comienzan con el encabezado

Código:
DEFINITION MODULE NombreDelModulo;


Como ven es muy distinto del encabezado de un módulo principal.
Otra diferencia es que un archivo de definición no lleva un BEGIN, pero sí debe terminar con un

Código:
END NombreDelModulo.


Nuestro archivo de definición tiene como nombre justamente Persona porque nosotros queremos crear un nuevo tipo llamado Persona.

Guardamos el nuevo archivo con exactamente el mismo nombre que le hemos puesto en el código, en este caso Persona, pero en vez de hacerlo en la carpeta SRC lo hacemos en la carpeta DEF de nuestro proyecto. Además le incluimos al archivo la extensión .def en vez de .mod.



Ahora verán coloreadas las palabras de su archivo. Debemos entonces agregar el nuevo archivo al proyecto. Esto es igual que agregar el módulo principal, vamos al menú

Project → Modify



Luego presionamos el botón Add:



Pueden escribir la dirección donde se encuentra el archivo de definición o pueden presionar sobre Browse para buscarlo. Yo elijo el botón Browse para sí abrir un cuadro de diálogo para buscar el archivo tranquilamente.

Hecho eso podrán ver el nuevo archivo añadido a la lista:



Presionan en OK Ahora debe actualizar la lista de archivos mediante el meú

Tools → Update file list



Con lo cual verán a su izquierda los archivos que componen su proyecto, en este caso son solo dos: El módulo principal y el archivo de definición del módulo Persona.

Hemos completado la creación del archivo de definición del módulo Persona.

Bien, como ustedes vieron cuando les hablé de las librerías predefinidas, un archivo DEF contiene toda la información pública de lo que hace una librería. Nosotros programaremos una librería llamada Persona, por lo tanto debemos proveer en el DEF qué es lo que hace nuestra librería pero no el como. Esto corresponde a definir una interfaz pública.

Una interfaz es un archivo (en realidad es un código específico) que describe un comportamiento acerca de algo pero no dice como se implementa dicho comportamiento. Entonces, los DEF son en realidad interfaces. Decimos que son públicas porque son accesibles para todo el mundo.
-------------------------------------------------------------------------------------

Tipo opaco:

Nuestro archivo .def entonces tendrá ahora este código:

Código:
DEFINITION MODULE Persona;
TYPE
    Persona; (*Es un tipo opaco.*)

    PROCEDURE CalcularSueldoGanado(p: Persona): REAL;
END Persona.


Entonces, lo que hemos hecho es declarar un tipo llamado Persona. Lo raro es que dicho tipo no tiene un signo de igual que indique qué es. Ahí no dice que eso sea un ARRAY, un RECORD, un subrango... nada. Justamente esa es la idea. Lo que ahí estamos haciendo es decir que existirá un tipo llamado Persona pero no decimos cómo funciona ¿si? Por eso se llama opaco.

Entonces, para que quede claro: No es el nombre del módulo lo que define el nombre del tipo que estamos creando, sino el tipo que creamos dentro mediante la ya conocida palabra reservada TYPE. El hecho de que el tipo y el módulo se llamen ambos Persona, es para que quede prolijo, pero bien podríamos haber llamado al archivo ModuloPersona y al tipo Persona ¿se entiende? Nosotros siempre haremos que coincida el nombre del módulo con el nombre del tipo que estamos definiendo, esa es una convención de programación.

Será luego, en el archivo de implementación donde diremos como funciona el tipo Persona.

Luego de la declaración del tipo tenemos la firma de la operación que podemos hacer dado un elemento del tipo Persona. Allí ven a la función REAL CalcularSueldoGanado que recibe como parámetro algo de tipo Persona y retorna un REAL. No está especificado allí cómo funciona la función. Esto es lo mismo que ustedes tenían cuando miraban las librerías de Modula. Justamente estamos programando nuestra propia librería.

Modula nos permite utilizar el tipo opaco en las definiciones que damos a pesar de que este en realidad aún no está definido, solo estamos diciendo que existirá un tipo Persona que será implementado en el módulo de implementación.
-------------------------------------------------------------------------------------

Módulo de implementación:

Teniendo entonces al módulo de definición completo, debemos terminar de armar el módulo Persona creando su archivo de implementación. Para esto creamos entonces un nuevo archivo como ya saben hacerlo y le incluimos a este siguiente código:

Código:
IMPLEMENTATION MODULE Persona;

END Persona.


Como primera cosa super importante es que el encabezado de un archivo de implementación que no es el módulo principal es siempre

Código:
IMPLEMENTATION MODULE NombreDelModulo;


El nombre que le damos debe ser exactamente igual al nombre de su archivo de definición. En este caso, como este módulo implementará al módulo Persona y su archivo de definición se llama justamente Persona, este archivo debe ser llamado así.

Otro punto importante es que un módulo de implementación tampoco lleva un BEGIN, así que ya podemos afirmar que solo el módulo principal lleva un BEGIN. Esto tiene sentido ya que en realidad es este módulo el que ejecuta el programa y luego hace llamados a los demás.

De este modo, el módulo de implementación correspondiente a uno de definición tendrá una estructura casi idéntica, es decir, tendrá todas las operaciones declaradas en el DEF pudiendo luego tener alguna más, así como también otros tipos. Todo irá quedando claro a medida que avancemos.

El archivo de implementación debe ser guardado dentro de la carpeta SRC con el mismo nombre que el archivo de definición salvo que su extensión será .mod. Entonces este archivo debe llamarse Persona.mod y debe ir dentro de la carpeta SRC.

Siempre los archivos .mod van dentro de SRC y los .def dentro de DEF.

Una vez guardado deberán agregarlo al proyecto y actualizar la lista de archivos.

Si ustedes intentan correr el programa ahora, tendrán el error en tiempo de compilación Unsatisfied opaque type “Persona”. Esto significa: Tipo opaco “Persona” insatisfecho. Lo que quiere decir es que Modula espera que definamos al tipo Persona que quedó sin definir en el DEF. Entonces, es aquí donde veremos cómo será dicho tipo.

Terminaremos siempre trabajando con registros. Los tipos opacos son siempre punteros a algo. No tenemos opción, un tipo opaco será siempre POINTER TO Algo. Esto tiene varios motivos:

  • La idea ese que la implementación no sea pública, sino el comportamiento. Por eso en el DEF no se ve cómo es el tipo, sino solo su nombre.
  • Obligando a usarlo como puntero, Modula puede reservar memoria en tiempo de compilación. Lo que se necesite luego para trabajar con el tipo será pedido en tiempo de ejecución porque es memoria dinámica.
  • Es una forma muy inteligente de separar la implementación del comportamiento y programar así librerías.


Entonces, en el módulo de implementación nos definimos un nuevo tipo registro que contendrá los datos de una persona y de este modo definiremos al tipo Persona como un puntero a ese registro:

Código:
IMPLEMENTATION MODULE Persona;

TYPE
    Persona= POINTER TO DatosPersona;

    DatosPersona= RECORD
        Nombre: TipoNombre;
        Edad: CARDINAL;
        SueldoPorHora: REAL;
        HorasTrabajadas: REAL;
    END;

END Persona.


Entonces, la idea es definir un registro que contiene los datos de una persona y luego definimos nuestro tipo Persona como puntero a ese registro. Bien podía haber sido distinto, no importa, si a mi se me ocurre en un futuro cambiar la implementación del tipo, no tengo ni por qué tocar a los módulos de definición, y de este modo, toda parte de un programa que use mi librería no debe ser modificada, solo cambiamos el módulo de implementación y listo. Esta es una de las enormes ventajas de dividir el programa en módulos.

Ahora bien, yo usé allí un tipo que no se sabe de donde sale: TipoNombre. Claramente dicho tipo será un ARRAY OF CHAR que contendrá el nombre de la persona. Podría haberlo definido allí mismo, pero no es la idea ya que se pretende que luego alguien pueda usar ese tipo para asignar el nombre de una persona. Entonces, declaro ese tipo en el archivo de definición para que sea público y accesible por todos aquellos que quieran usarlo. A su vez me definiré una constante para utilizar en la definición de TipoPersona:

Código:
DEFINITION MODULE Persona;
CONST
    MAX_LARGO_NOMBRE= 50;
TYPE
    Persona; (*Es un tipo opaco.*)
    TipoNombre= ARRAY[1..MAX_LARGO_NOMBRE] OF CHAR;

    PROCEDURE CalcularSueldoGanado(p: Persona): REAL;
END Persona.


Si ustedes van al menú Tools tienen la opción de compilar únicamente al archivo en que está trabajando, la cual es equivalente a presionar F9. Cuando se compila un archivo .def no genera un .exe sino que genera un archivo .sym que será guardado automáticamente en la carpeta SYM de nuestro proyecto. Esto lo utilizará el compilador luego para generar el programa total. Cuando se compila un archivo .mod se genera un archivo .obj que será guardado en la carpeta OBJ de nuestro proyecto.

Cuando vamos a Tools → Build all, el compilador compilará por separado cada archivo .def y cada archivo .mod de nuestro proyecto estableciendo las “uniones” que hay entre ellos, es decir, estableciendo las relaciones que hay entre cada módulo. Finalmente, entre todos ellos y el módulo principal creará un .exe que podrá ser ejecutado. En otras plataformas que no sean Windows no se generará un .exe sino que se generará un binario que dependerá de la plataforma. Los binarios de Windows son .exe.

Si ustedes intentan ahora compilar únicamente al archivo Persona.def no tendrán problemas y podrán ver al archivo Persona.sym creado en la carpeta SYM. Sin embargo, si intentan compilar a Persona.mod tendrán problemas. Yo siempre compilo todo junto cada vez que modifico algo, pero eso lo deciden ustedes. Por lo general, cada vez que cambian algo de un archivo, cuando intentan correr el programa el XDS se da cuenta de que hubieron cambios y les pregunta si quieren reconstruir todo el proyecto, lo cual equivale a ir a Tools → Build all. Entonces le dicen que SI y listo.

Si han ido siguiendo lo que he hecho, al intentar compilar al arhivo Persona.mod o a todo el proyecto en sí, verán el error: procedure not implemented “CalcularSueldoGanado”, lo cual significa: procedimiento “CalcularSueldoGanado” no implementado.
Lo que pasa es que en el archivo de definición del módulo Persona hemos declarado una operación llamada CalcularSueldoGanado dando su firma. Modula espera que esa función sea implementada justamente en el archivo de implementación. Los archivos de implementación deben implementar a TODAS las operaciones definidas en el archivo de definición.

Entonces debemos darle código a nuestra función para que realice sus tareas. En principio simplemente le daré un código tribial para poder compilar el proyecto, luego le daremos el código real que debe tener.
Aquí entonces volveremos a escribir la firma de la función TAL CUAL la escribimos en el archivo de definición y le daremos código tal como ya saben hacerlo.

Código:
IMPLEMENTATION MODULE Persona;

TYPE
    Persona= POINTER TO DatosPersona;

    DatosPersona= RECORD
        Nombre: TipoNombre;
        Edad: CARDINAL;
        SueldoPorHora: REAL;
        HorasTrabajadas: REAL;
    END;

PROCEDURE CalcularSueldoGanado(p: Persona): REAL;
BEGIN
    RETURN 0.0;
END CalcularSueldoGanado;

END Persona.


Bien, entonces esa función lo único que hace ahora es retornar el valor 0.0. Como verán, la forma de declarar y trabajar con la función sigue siendo la misma de siempre. Así que primero declaramos los tipos necesarios y luego las operaciones a implementar.

El tipo TipoNombre, a pesar de no estar declarado allí, no nos da problemas porque Modula lo busca en el .def y, al encontrarlo, lo utiliza en el .mod. Es por esto que archivos de definición y archivos de implementación conforman un mismo módulo, porque trabajan como una sola entidad.

Bien, escribamos entonces el código correcto a la función:

Código:
PROCEDURE CalcularSueldoGanado(p: Persona): REAL;
BEGIN
    RETURN p^.SueldoPorHora*p^.HorasTrabajadas;
END CalcularSueldoGanado;


En este código nosotros hemos accedido diréctamente a la representación del tipo Persona porque sabemos justamente como está implementado. Sin embargo, si luego se nos antoja cambiar la implementación de dicho tipo tendremos que cambiar también el código de la función. Para este caso, como solo tenemos una función, no hay forma de evitar esto. En efecto, no hay forma de evitarlo, siempre dependeremos en alguna medida de la implementación del tipo y si la cambiamos tendremos que cambiar cosas, sin embargo, hay métodos para minimizar esto lo más posible. Los iremos viendo de a poco.
Bien, habiendo puesto ese código fuente en la función ya podemos recompilar, incluso podemos correr nuestro programa, solo que no hará nada porque no hay nada en el código fuente del módulo principal.

La única operación que tenemos aquí corresponde a la categoría de operaciones SELECTORAS ya que permite obtener un valor. Ahora bien, todo lindo ¿no? Tenemos nuestro tipo persona que tiene Nombre, Edad, Horas trabajadas, y Sueldo por hora. ¿Cómo hacemos para crear un objeto Persona y darle datos?

Eso corresponde a otra categoría de operaciones, las CONSTRUCTORAS.

Lo veremos en la próxima lección. Por ahora tienen bastante con lo que hemos visto hasta el momento. Saludos.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
juan alejandro
Usuario Iniciado


Registrado: 07 Ene 2011
Mensajes: 43
Ubicación: Bogotá - Colombia

Mensaje Publicado: Martes 13 Dic 2011 14:26

Título del mensaje: Re: Programando desde 0: 40- Introducción a los TADs.

Responder citando

Muchas gracias kyshuo, está muy interesante la lección.

Volver arriba
Ver perfil del usuario Enviar mensaje privado
sr floyd
Usuario Activo


Registrado: 05 Sep 2011
Mensajes: 196

Mensaje Publicado: Domingo 07 Oct 2012 09:52

Título del mensaje: Re: Programando desde 0: 40- Introducción a los TADs.

Responder citando

Magnífica lección!! Ok

Volver arriba
Ver perfil del usuario Enviar mensaje privado MSN Messenger
Responder al Tema
Mostrar mensajes anteriores:   
Ir a:  
Todas las horas están en GMT + 2 Horas

Temas relacionados

Tema Autor Foros Respuestas Publicado
El foro no contiene ningún mensaje nuevo

Buenas desde el sur del sur =)

Maugarni Preséntate a la comunidad 1 Jueves 22 Ago 2019 14:09 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Hola desde bcn

Dav2k6 Preséntate a la comunidad 2 Miércoles 26 Jun 2019 19:22 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Existen problemas al descargar musica desde you...

SusanaP Tu PC 2 Martes 26 Mar 2019 19:22 Ver último mensaje
El foro no contiene ningún mensaje nuevo

hola!! los saludo desde argentina

mery Preséntate a la comunidad 2 Jueves 13 Dic 2018 17:28 Ver último mensaje
El foro no contiene ningún mensaje nuevo

Llamada a web service desde form

mrrobot2 Programación Web en general 1 Martes 14 Nov 2017 00:50 Ver último mensaje
Panel de Control
No puede crear mensajes, No puede responder temas, No puede editar sus mensajes, No puede borrar sus mensajes, No puede votar en encuestas,