UTN.- Paradigmas de Programación. 2009 Paradigmas de Programación Programación Procedural. TAD’s. Repaso de las principales características de la programación procedural. Características de los lenguajes imperativos: Son los lenguajes orientados a “sentencias”. La unidad de trabajo es la sentencia. Los efectos de las sentencias individuales se combinan en un programa para obtener los resultados deseados. Todos estos lenguajes se basan fundamentalmente en la arquitectura tradicional de la computadora. Esto se puede ver en tres características principales: • variables: La componente principal de la arquitectura es la memoria, que consta de un gran número de celdas. Es el lugar donde se almacenan los datos. Los valores se almacenan en las celdas y se puede acceder a ellas dando un nombre a dichas celdas. Esto está representado por el concepto de variable. • operación de asignación: Estrechamente ligado a la arquitectura de la memoria se encuentra la idea de que el valor calculado debe ser “almacenado”, es decir, asignado a una celda. Esta es la razón de la importancia de la sentencia de asignación. • repetición: un programa en un lenguaje imperativo, normalmente realiza su tarea ejecutando repetidamente una secuencia de pasos elementales. Esto es una consecuencia de la arquitectura, en la cual las instrucciones se almacenan en memoria y la única manera de llevar a cabo algo complicado es repitiendo una secuencia de instrucciones. El papel de la abstracción Como cualquier modelo, un programa es una abstracción de la realidad. recordamos que, la abstracción es el proceso de identificación de las propiedades importantes del problema que se está modelando, ignorando los detalles irrelevantes. La abstracción es el concepto clave en la teoría de la programación. En concreto, tiene una doble relación con los lenguajes de programación. Por un lado, los lenguajes son las herramientas con las que los diseñadores pueden implementar los modelos abstractos (“los programas”). Por el otro, ellos mismos son abstracciones del procesador sobre el cual se implementa el modelo. Sin embargo, los primeros lenguajes no reconocían el papel crucial que la abstracción juega en la programación. Por ejemplo, a comienzos de la década del ‘50, el único mecanismo de abstracción proporcionado por los lenguajes (ensambladores) sobre los lenguajes de máquina era la denominación simbólica: se utilizaban mnemotécnicos o términos relativamente autoexplicativos para denominar códigos de operación y posiciones de memoria. TADs - Página 1 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Los subprogramas fueron también introducidos en los lenguajes de tipo ensamblador como un mecanismo para denominar una actividad descrita por un grupo de acciones y considerarla como una única acción. Los subprogramas son mecanismos útiles para la construcción de abstracciones. Un subprograma es la implementación de una abstracción mientras que la llamada a un subprograma representa el uso de la abstracción. Cuando el programador implementa un subprograma se encuentra en el cómo trabaja dicho subprograma. Cuando se invoque este subprograma, ya no nos interesa el “cómo” sino el “qué” hace. Esto se conoce como abstracción procedural. También es posible definir abstracciones de datos. Las abstracciones de datos modelan los datos manipulados por el programa. Abstracciones de datos. Cómo aparecen en los lenguajes. Los lenguajes a nivel de la máquina veían a los datos almacenados como cadenas de bits que podían ser manipulados por las instrucciones de la máquina. El repertorio de instrucciones incluía operaciones de desplazamientos, lógicas, aritméticas en pto. flotante, etc. Fortran, Cobol y Algol 60 dieron su primer paso hacia la introducción a la abstracción de datos. En estos lenguajes, la información almacenada en determinadas posiciones de memoria no se ve como una secuencia de bits anónimos, sino como un valor entero, real, lógico, etc. Cada lenguaje tenia un propósito fijo y de acuerdo a esto proveía los tipos adecuados. Como resultado, ninguno de estos lenguajes resulto apropiado para TODAS las aplicaciones, ya que el programador estaba limitado por la rigidez del conjunto fijo de abstracciones proporcionado por el lenguaje. El objetivo perseguido por los lenguajes de la siguiente generación (Simula 67, Algol 68, Pascal) era incorporar mecanismos flexibles y fáciles de usar por medio de los cuales el programador pueda definir nuevas abstracciones. Algol 68 y Pascal permiten al programador usar tipos de datos incorporados y constructores de nuevos tipos (matrices, registros). Ejemplo: En Pascal: type estudiante=record nombre:string[30]; legajo:integer; end; curso=array[1..60] of estudiante; var grupo_A:curso; El tipo estudiante es una estructura de datos que consta de dos componentes utilizado para guardar una identificación de un estudiante. El tipo curso se define como una colección (arreglo) de estudiantes. TADs - Página 2 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Si quiero manipular los estudiantes de un curso, podría definir procedimientos tales como: • Insertar(grupo_A, unEstud); • Eliminar(grupo_A, unEstud); • Imprimir(grupo_A); • etc. Estos procedimientos están fuertemente relacionados con el tipo curso. En concreto, son las operaciones que manipulan los objetos del tipo curso. Sin embargo, esta relación lógica no queda evidente en el programa: Ejemplo: program demo; type ............ // se mezclan con otros tipos var ............ // se mezclan con otras variables Proc,.... ............ // se mezclan con otros procedimientos begin ............ end. Hay semejanzas entre los tipos definidos por el usuario de Pascal y los tipos predefinidos. Por ejemplo, consideremos el tipo predefinido “integer” y el tipo definido por el usuario “curso”. Los dos tipos son abstracciones construidas sobre una representación interna: una cadena de bits para los integer y un arreglo de registros para curso. Los dos tipos tienen asociados un conjunto de operaciones: operaciones aritméticas y de comparación para los integer e Insertar(..), Eliminar(..), Imprimir(..), etc. para curso. Sin embargo, los tipos predefinidos y los tipos definidos por el usuario difieren uno del otro en un aspecto muy importante. Los tipos predefinidos ocultan al programador la representación interna: ésta no puede ser manipulada directamente. Por ejemplo, el programador no puede acceder a un bit en particular de la cadena de bits que representan al integer (si no dispone de operaciones provistas para tal fin, obviamente). Por otra parte, los procedimientos Insertar(..), Eliminar(..), Imprimir(..), etc., no son los únicos medios para manipular un objeto de tipo curso. El programador puede operar directamente sobre cada registro que compone el arreglo, sin estar obligado a utilizar exclusivamente las operaciones definidas para el nuevo tipo. Ejemplo: grupo_A[15].legajo:=12345; En otras palabras, desde el punto de vista del lenguaje, no hay distinción entre dos niveles de abstracción: el nivel en el que se pueden utilizar cursos como objetos nuevos y el nivel en el que se puede considerar la representación de los cursos en términos de las abstracciones de mas bajo nivel. TADs - Página 3 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Esta confusión entre niveles de abstracción puede conducir a la producción de programas difíciles de leer y lo que es más importante, reduce la modificabilidad de los mismos. Por ejemplo, si cambiamos la representación interna de los cursos a listas encadenadas necesitaríamos cambiar todos los accesos directo a la representación interna (como la sentencia anterior) a los largo de todo el programa. En conclusión, si queremos definir nuevos tipos de datos en un programa, seria deseable que el lenguaje me permitiera asociar una representación con las operaciones para manipularla en una unidad adecuada del lenguaje (“separada” del programa) y ocultar la representación interna del nuevo tipo a las unidades que la usa. Los tipos definidos por el usuario que satisfacen estas dos propiedades se denominan “tipos abstractos de datos” (TAD). La primer propiedad hace que la versión final del programa refleje las abstracciones descubiertas durante la fase de diseño. La segunda propiedad enfatiza la distinción entre los niveles de abstracción y favorece la modificabilidad de los programas. Pascal, en su definición original, no proveía ningún mecanismo para la construcción de TAD’s. Turbo Pascal tiene herramientas para cubrir la primer propiedad (encapsular la definición del tipo con las operaciones para manipularlo en una “unit”) pero no para ocultar la estructura interna del tipo. Más adelante vamos a ver otro lenguaje (ADA) que provee mecanismos que cubren las dos propiedades. Ahora vamos a ver cómo se diseña utilizando TAD’s, cómo se identifica un TAD en un problema. Antes vamos a recordar algunos principios de la abstracción procedural o la técnica de diseño por refinamientos sucesivos.. REPASO: Abstracción de procedimientos (refinamientos sucesivos) Es el principio subyacente de la programación utilizando el diseño topdown (refinamientos sucesivos). Las ventajas de este estilo de programación ya las vimos en el semestre pasado (en la materia Sintaxis y ..), pero vamos a recordarlos: “es un método que hace que la complejidad de un programa grande sea manejable, controlando sistemáticamente la interacción entre sus componentes”. Esto es, un programa modular es más fácil de escribir, ya que enfocamos nuestra atención en una tarea (procedimiento o función) a la vez. La idea es que sabe “qué” es lo que hace un módulo y no “cómo” lo hace. O sea, desde afuera NO PUEDO (no interesa) ver detalles de implementación de ese módulo. Es como si estaría rodeado de “paredes” (walls) que impiden que otros módulos sepan (vean) cómo fue implementado. Para poder utilizar los módulos debemos especificar la forma en que se interactúa con ellos, es decir debemos establecer un “contrato” en el cual se especifica cómo se lo debe TADs - Página 4 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 invocar (con qué parámetros, de qué tipo, etc.) y qué se obtendrá como resultado luego de ejecutar el módulo. Todo esto no es nuevo. Es lo que vimos anteriormente sobre refinamientos sucesivos, solo que ahora “formalizamos” un poquito más el concepto. Programa que usa una tarea Implementa request retorno de la op. ción de la tarea La abstracción procedural particiona un programa en algoritmos independientes, los cuales ejecutan tareas más pequeñas. Se piensa en términos de qué es lo que un algoritmo hace, independientemente de cómo lo hace. En la abstracción de datos se piensa qué puedo hacer con una colección de datos independientemente de cómo lo voy a hacer. La abstracción de datos es una herramienta que nos permite desarrollar las estructuras de datos en una forma relativamente independiente del resto de la solución. Los otros módulos de la solución conocerán qué operaciones pueden hacer con los datos pero no cómo están almacenados ni cómo se ejecutarán las operaciones. Ya vimos qué era un TAD: una colección de datos, más el conjunto de operaciones para manipularlos. La definición de las operaciones debe ser rigurosa, para especificar completamente el efecto que tiene sobre los datos, pero no se especificará si los datos se almacenarán en posiciones consecutivas de memoria, o en posiciones disjuntas, ni nada. Cuando se implementa el TAD se elige una representación particular, una estructura de datos (recordemos que son construcciones que provee el lenguaje para guardar datos, por ejemplo, un array en Pascal). Un TAD no es lo mismo que una estructura de datos. Por ejemplo, la estructura de datos “curso” definida anteriormente no es un TAD. Cuando un programa debe ejecutar operaciones sobre sus datos que no están soportadas directamente por el lenguaje, se debe construir la estructura de datos. Primero debería diseñar el TAD y entonces cuidadosamente especificar las operaciones (el “contrato”). Entonces (y sólo entonces), debería implementar las operaciones con una estructura de datos. Si se implementan las operaciones apropiadamente, el resto del programa será capaz de ejecutar las operaciones según fueron especificadas. TADs - Página 5 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 En resumen, se usa el mismo concepto que para la abstracción procedural: hay “paredes” (“walls”) entre un programa y sus estructuras de datos. El “contrato” define qué operaciones puedo realizar con la estructura de datos y qué resultados obtengo luego de ejecutar esas operaciones. Programa que usa la estructura de datos request return Implementa ción de la estructura de datos Ejemplo: TAD lista ordenada Consideremos una lista cualquiera (la lista del super, por ejemplo). harina arroz gaseosas huevos yerba azúcar ....... El orden está dado por el orden en el cual se me fueron ocurriendo las cosas (no necesariamente es un orden alfabético, sólo que un ítem tiene un anterior y un siguiente) Ahora pensemos, qué cosas podría hacer con la lista? • contar los ítems para saber cuántas cosas tengo que comprar (la longitud de la lista); • agregar un elemento en la pos. i de la lista; • eliminar el elemento de la posición i; • ver el ítem que está en la posición i. Cómo especificamos estas operaciones? Procedure Crear(L) { Crea una lista ordenada L vacía} Function Longitud(L) : integer TADs - Página 6 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 { Retorna el numero de ítems que están en la lista ordenada L} Procedure Insertar(L, i, nuevo-item) { Inserta el nuevo-item en la posición i de la lista. El valor de i debe estar en el rango de 1 a Longitud(L)+1. Si i<=Longitud(L), los ítems son desplazados como sigue: el ítem de la posición i pasa a la posición i+1, el de la posición i+1 a la i+2, etc. y el de la posición Longitud(L), pasa a la posición Longitud(L)+1. } Procedure Eliminar(L,i) { Elimina el ítem de la posición i de la lista ordenada L. El valor de i debe estar en el rango de 1 a Longitud(L). Si i<Longitud(L), los ítems son desplazados como sigue: el ítem de la posición i+1 pasa a la posición i, el de la posición i+2 a la i+1, etc. y el de la posición Longitud(L), pasa a la posición Longitud(L)-1. } Function Recuperar(L, i) : <tipo correspondiente a los ítems> { Retorna el ítem que se encuentra en la posición i de la lista ordenada L. El valor de i debe estar en el rango de 1 a Longitud(L). La lista no cambia luego de esta operación (esta operación no altera la lista)} Las especificaciones de estas 5 operaciones son los términos del “contrato” para el TAD lista ordenada. Notemos que estas especificaciones no mencionan cómo se almacena la lista ordenada ni cómo se implementan las operaciones. La definición del TAD únicamente dice lo que se puede hacer con una lista ordenada. Ejemplo: Si queremos hacer una pequeña aplicación que intercambie dos elementos de la lista que se encuentran en las posiciones i y j respectivamente: Procedure Cambiar(L, i, j) mo. mo. { cambia el i y el j elemento de la lista ordenada L} mo. primero:= Recuperar(L, i); {Copia el i ítem} segundo:= Recuperar(L, j); {Copia el j mo. ítem} Eliminar(L.i); Insertar(L, i, segundo); { Reemplaza el i mo. ítem con el j mo. Eliminar(L.j); Insertar(L, j, primero); { Reemplaza el j mo. ítem con el i mo. } } Notar que esta aplicación NO necesita saber cómo está implementada la lista. TADs - Página 7 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Diseñando un TAD El diseño de un TAD surge naturalmente durante el proceso de la resolución del problema. Cuando Pascal (o el lenguaje que se va a utilizar) no provee los tipos convenientes y un tipo definido por el usuario no es suficiente, ya que no se puede definir las operaciones y las restricciones, entonces se piensa en un TAD. Ejemplo: Supongamos que queremos imprimir todas las fechas que sean feriados del almanaque de un determinado año. Una posible solución sería: tomar la fecha correspondiente al primer día del año while la fecha no corresponda con el último dia del año do begin if la fecha corresponde a un día feriado then Imprimirla tomar la siguiente fecha end; En este problema aparace un tipo no provisto por Pascal: el tipo fecha. Una fecha contiene dia, mes y año. ¿Qué operaciones serían necesarias? • determinar la fecha correspondiente al 1er. día del año. • determinar si una fecha corresponde al último día del año. • determinar si una fecha es o no feriado. • dado una fecha, determinar la fecha siguiente. ¿Cómo las especificamos? Function PrimerDia(): fecha {Retorna la fecha correspondiente al primer dia del año} Function EsUltimoDia(unaFecha): boolean {Retorna verdadero si unaFecha corresponde al último día del año} Function EsFeriado(unaFecha): boolean {Retorna verdadero si unaFecha corresponde a un día feriado} Function SiguienteFecha(unaFecha): fecha {Retorna la fecha del dia siguiente al la fcha dada} ¿Cómo quedaría la solución a nuestro problema en función del TAD fecha? dia:= PrimerDia(); while not EsUltimoDia(dia) do begin if EsFeriado(dia) then Imprimir(dia) (***) dia:= SiguienteFecha(dia) end; TADs - Página 8 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 (***) En realidad, en este punto, nos damos cuenta que no podemos imprimir un dato tipo fecha (porque no conocemos nada de él). Por lo tanto, necesitamos definir más operaciones al TAD que no devuelvan el dia, mes y año de una fecha dada y luego imprimir estos valores. Function Dia(unaFecha): integer { Retorna el día de la fecha dada. El día estará en el rango de 1 a 31} Function Mes(unaFecha): integer { Retorna el mes de la fecha dada. El mes estará en el rango de 1 a 12} Function Anio(unaFecha): integer { Retorna el año de la fecha dada. El año estará en el rango de 0 a 2500} Anteriormente diseñamos el TAD en función del problema/aplicación a resolver. También podemos disñar el TAD y luego utilizarlo en alguna aplicación (hacer el proceso inverso) Ejemplo: Vamos a diseñar el TAD “Agenda”. ¿Qué operaciones necesitamos proveer para manejar una agenda? Las más obvias: • agendar una actividad en un dia y hora determinado • desagendar una acctividad previamente agendado en un dia y hora determinado Podemos pensar en otras dos operaciones que complementan las anteriores. • ver si hay una actividad agendad en un horario y dia determinado • ver qué actividad está agendada en un horario y dia determinado Estas dos operaciones podríamos combinarlas y especificarlas como una sola operación. Especificaciones: Function CrearAgenda(): agenda { Retorna una agenda vacía} Procedure Agendar(unaAgenda, fecha, hora, actividad) { Agrega en la agenda la actividad en el dia y hora dado asumiendo que ese horario está libre} Procedure Desagendar(unaAgenda, fecha, hora) { Borra la actividad agendada en la fecha y hora dada} Procedure VerActividad(unaAgenda, fecha, hora, hayActividad, unaActividad) {determina si existe o una actividad agendada en la fecha y hora dada. Si hay, setea hayAct en true y retorna la actividad agendad en unaActividad. Si no hay nada agendado setea hayAct en false} Aplicación: TADs - Página 9 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Supongamos que queremos cambiar el día y hora de una actividad particular (esto que lo hagan ellos - a lo sumo en la práctica) Leer(diaAnt, horaAnt, diaNuevo, horaNueva,); VerActividad(agenda, diaAnt, horaAnt, hayAct, acti); if hayAct { verifico que verdaderamente estaba algo agendado} then begin VerActividad(agenda, diaNuevo, horaNueva, hayAct, acti); if hayAct { si ya tengo algo agendado en ese horario} then write(“Ud. ya tiene agendada otra actividad en el nuevo horario”) else { el horario está libre} begin Agendar(agenda, diaNuevo, horaNueva, acti); Desagendar(agenda, diaAnt, horaAnt); end; end; else write(“Ud. no tiene agendada ninguna actividad en ese horario”); Al diseñar TAD’s nos podemos encontrar con otros TAD’s. por ejemplo, en el TAD anterior (agenda) hacemos referencia a fechas, y éstas podrían ser datos abstractos. Esto es, se puede utilizar un TAD para implementar otro TAD. Otro Ejemplo: queremos armar una base de datos de recetas (un recetario). Las operaciones para manipularlo sería: Procedure Crear(recetario) {Crea un recetario vacío} Procedure Insertar(recetario, receta) {Agrega la receta al recetario. Se asume que la receta NO está en el recetario} Procedure Eliminar(recetario, receta) {Elimina la receta del recetario. Asume que la receta está en el recetario} Function Recuperar(recetario, nombreReceta): Receta {Retorna la receta del recetario cuyo nombre coincide con nombreReceta} Function Esta(recetario, nombreReceta):Boolean { Retorna true si la receta cuyo nombre es nombreReceta está en el recetario y false en caso contrario} En este caso, podríamos pensar en definir otro TAD: Receta que contengan la información de una receta, con operaciones como: Function Nombre(receta): string[50] {Retorna el nombre de la receta} Esta función sería utilizada por las operaciones Recuperar y Esta para obtener el nombre de las recetas y compararlos con el nombreReceta recibido como parámetro en el momento de implementar el TAD recetario. TADs - Página 10 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Implementación de TAD’s Tenemos que ver cómo guardar los datos y cómo realizar las operaciones especificadas anteriormente. TAD Lista Ordenada. Tenemos dos opciones para almacenar los datos: • utilizar arreglos • utilizar punteros (listas encadenadas) Recordamos la especificación del TAD: Procedure Crear(L) { Crea una lista ordenada L vacía} Function Longitud(L) : integer { Retorna el numero de ítems que están en la lista ordenada L} Procedure Insertar(L, i, nuevo-item) { Inserta el nuevo-item en la posición i de la lista. El valor de i debe estar en el rango de 1 a Longitud(L)+1. Si i<=Longitud(L), los ítems son desplazados como sigue: el ítem de la posición i pasa a la posición i+1, el de la posición i+1 a la i+2, etc. y el de la posición Longitud(L), pasa a la posición Longitud(L)+1. } Procedure Eliminar(L,i) { Elimina el ítem de la posición i de la lista ordenada L. El valor de i debe estar en el rango de 1 a Longitud(L). Si i<Longitud(L), los ítems son desplazados como sigue: el ítem de la posición i+1 pasa a la posición i, el de la posición i+2 a la i+1, etc. y el de la posición Longitud(L), pasa a la posición Longitud(L)-1. } Function Recuperar(L, i) : <tipo correspondiente a los ítems> { Retorna el ítem que se encuentra en la posición i de la lista ordenada L. El valor de i debe estar en el rango de 1 a Longitud(L). La lista no cambia luego de esta operación (esta operación no altera la lista)} Implementación basada en arreglos. ................. . Longitud Items Podría definir la lista como un registro con dos campos:la longitud de la lista y los ítems en sí. const type maxlen=100; itemType=<tipo de los items de la lista> listaOrdenada=record long:integer; TADs - Página 11 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 items:array[1..maxlen] of itemType; end; var L:listaOrdenada; algunas de las operaciones quedarían: Procedure Crear (var L: listaOrdenada) { Crea una lista ordenada L vacía} begin L.Long:=0; end; Function Longitud(L:listaOrdenada): integer; { Retorna el numero de ítems que están en la lista ordenada L} begin Longitud:=L.long; end; Procedure Insertar(var L: listaOrdenada; i:integer; nuevoItem:itemType) { Inserta el nuevo-item en la posición i de la lista. El valor de i debe estar en el rango de 1 a Longitud(L)+1. Si i<=Longitud(L), los ítems son desplazados como sigue: el ítem de la posición i pasa a la posición i+1, el de la posición i+1 a la i+2, etc. y el de la posición Longitud(L), pasa a la posición Longitud(L)+1. } begin if ((i<1) and i>Longitud(L)+1) or (Longitud(L)=maxlen) then Indicar que hay un ERROR else begin {desplazar los ítems} for j:= Longitud(L) downto i do L.items[j+1]:=L.items[j]; { Insertar el nuevo item} L.items[i]:= nuevoItem; L.long:=L.long+1; end; end; ………………… Implementación basada en punteros (listas encadenadas) long head item sgte Para no tener la restricción del tamaño fijo del arreglo. type itemType=<tipo de los items de la lista> ptr = .. nodo; nodo =record item: itemtype; sigte:ptr; end; TADs - Página 12 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 listaOrdenada=record long:integer; head:ptr; end; var L:listaOrdenada; las operaciones quedarían: Procedure Crear (var L: listaOrdenada) { Crea una lista ordenada L vacía} begin L.long:=0; L.head:=nil; end; Function Longitud(L:listaOrdenada): integer; { Retorna el numero de ítems que están en la lista ordenada L} begin Longitud:=L.long; end; Para implementar las otras operaciones necesitamos acceder a la posición i de la lista, cosa qu la estructura elegida no me lo provee (las listas enganchadas no tienen acceso directo a sus componenetes). Por lo tanto, podríamos pensar en definir una operación propia del TAD llamasda SetPtr, que retorna el puntero al nodo que se encuentra en una posición especificada. Otra vez, esta operación NO estaría disponible para las aplicaciones que usen la lista ordenada, sino que la es “privada” del TAD, me sirve para implementar las operaciones restantes. Function SetPtr(L: listaOrdenada; i:integer):ptr {Retorna un puntero al nodo que se encuentra en la posición i. Si i<1 o i>Longitud(L), retorna un puentero nulo} begin if (i<1) or (i>Longitud(L)) then SetPtr:= nil else begin p:=L.head; for j:=1 to i-1 do p:=p..sigte; SetPtr:=p; end; end; algunas de las operaciones quedarían: TADs - Página 13 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com UTN.- Paradigmas de Programación. 2009 Function Recuperar (L: listaOrdenada; i:integer) { Retorna el ítem que se encuentra en la posición i de la lista ordenada L. El valor de i debe estar en el rango de 1 a Longitud(L). La lista no cambia luego de esta operación (esta operación no altera la lista)} begin if (i<1) or i>Longitud(L) then Indicar que hay un ERROR else begin p:=SetPtr(i); Recuperar := p..item; { No testeo por p<>nil porque ya lo testee al ppio.} end; end; …………………… TADs - Página 14 PDF Created with deskPDF PDF Writer - Trial :: http://www.docudesk.com