Apunte Teorico(AlgoritmosI)(VERSION3)

218
APUNTE TEÓRICO 3ra. EDICIÓN JUNIO DE 2002 Cátedra Lic. Gustavo López Facultad de Ingeniería – Universidad de Buenos Aires Lic. Gustavo López Augusto Vega Matías Gavinowich Enrique Calot Incluye Manejo de Punteros y Memoria Dinámica, y Unidades de Biblioteca.

Transcript of Apunte Teorico(AlgoritmosI)(VERSION3)

Page 1: Apunte Teorico(AlgoritmosI)(VERSION3)

A P U N T E T E Ó R I C O 3 r a . E D I C I Ó N

J U N I O D E 2 0 0 2

Cátedra Lic. Gustavo López Facultad de Ingeniería – Universidad de Buenos Aires

Lic. Gustavo LópezAugusto Vega

Matías GavinowichEnrique Calot

Incluye Manejo de Punteros y Memoria

Dinámica, y Unidades de

Biblioteca.

Page 2: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 2

Indice

Pág.

Capítulo I: Introducción a Pascal 3 Capítulo II: Procedimientos y Funciones 26 Capítulo III: Vectores y Matrices 45 Capítulo IV: Métodos de Búsqueda 59 Capítulo V: Registros 67 Capítulo VI: Archivos 85 Capítulo VII: Operaciones entre Archivos 128 Capítulo VIII: Claves 141 Capítulo IX: Índices 152 Capítulo X: Recursividad 163 Capítulo XI: Manejo de Punteros y Memoria Dinámica 173 Capítulo XII: Unidades de Biblioteca 190 Apéndice A: Pseudocódigo 205 Apéndice B: Diagramas de Flujo 215

Page 3: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO I

Introducción a Pascal

Page 4: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 4

INTRODUCCIÓN A PASCAL

Impresionante es el éxito de Pascal en todo el mundo desde su introducción a comienzos de los años 70. Fue diseñado por Niklaus Wirth en el Instituto Federal de Tecnología de Zurich. El espíritu original de Wirth se mantiene “ ... disponer de un lenguaje adecuado para enseñar programación como una disciplina sistemática basada en determinados conceptos fundamentales, clara y naturalmente reflejados por el lenguaje ...” En pocos años se ha convertido en el lenguaje estándar para estudiantes de programación en la mayoría de las universidades. Muchos profesionales de la informática están recurriendo a Pascal con preferencia a otros lenguajes que pudieran ser más fácilmente accesibles. En definitiva, ningún otro lenguaje de programación ha tenido una influencia tan grande sobre la comunidad informática en un período tan corto de tiempo. Este interés en Pascal proviene de varias razones: Primero, porque Pascal es un lenguaje con estilo. En efecto, Pascal fue diseñado específicamente para promover un método disciplinado y elegante de programar computadoras. Su utilización estimula el desarrollo de programas bien organizados, claramente escritos y relativamente libre de errores. Además el lenguaje está dispobible prácticamente para cualquier computadora. Y puede ser usado eficazmente en cualquier entorno de programación, desde el procesamiento batch hasta el procesamiento altamente interactivo. Pascal ofrece, entonces, toda una serie de características deseables, relativamente inalcanzables con otros lenguajes de programación. Las versiones más populares y extendidas en el mundo del lenguaje Pascal son dos: Pascal estándar, que recoge la versión de Wirth normalizada por las organizaciones ANSI e ISO , y Turbo Pascal, producto desarrollado y totalmente depurado de la casa BORLAND, que sin lugar a dudas, es la versión más utilizada tanto en el campo de la enseñanza como en el mundo profesional y de investigación. La principal razón para que las personas aprendan lenguajes de programación es utilizar la computadora como una herramienta para la resolución de problemas. Dos fases pueden ser identificadas en el proceso de resolución de problemas ayudados por computadora. 1.- Fase de resolución del problema 2.- fase de implementación (realización) en la computadora El resultado de la primera fase es el diseño de un algoritmo para resolver el problema. Un algoritmo es un conjunto de instrucciones que conducen a la solución del problema. El algoritmo se puede expresar en lenguaje castellano o en cualquier otro lenguaje como el portugués, francés, italiano o, el lenguaje por excelencia de las computadoras, el inglés. Es frecuente en la comunidad de habla castellana la descripción del algoritmo en lenguaje español, pese a que todos los compiladores e intérpretes de lenguajes de programación tengan sus palabras reservadas en inglés.

Page 5: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 5

El algoritmo se expresa en un lenguaje de programación que la computadora pueda comprender. Dicho algoritmo expresado en cualquier lenguaje de programación de computadoras se denomina programa. La ejecución y verificación del programa en una computadora es el objetivo final de la fase de implementación o realización. El diseño de programas es una tarea difícil y es un proceso creativo. No existe un conjunto completo de reglas, ni algoritmos para indicar cómo escribir programas. Las fases mencionadas anteriormente disponen de una serie de pasos que enlazados convenientemente conducirán a la solución del problema. Aunque el diseño de programas es un proceso esencialmente creativo, se pueden considerar una serie de fases o pasos comunes que generalmente deben seguir todos los programadores. Las fases de resolución de un problema con computadora son: Ø Análisis del problema Ø Diseño del algoritmo Ø Codificación Ø Compilación y ejecución Ø Verificación Ø Depuración Ø Documentación

Las dos primeras fases conducen a un diseño detallado escrito en forma de algoritmo. Durante la tercera etapa (codificación) se implementa1 el algoritmo en un código escrito en un lenguaje de programación, reflejando las ideas desarrolladas en las fases de análisis y diseño. La fase de compilación traduce el código fuente a código máquina mediante el empleo de intérpretes o compiladores y en la fase de ejecución se corre el programa sobre la computadora. En las fases de verificación y depuración el programador busca errores de las etapas anteriores y los elimina. Podrá comprobar que mientras más tiempo gaste en la fase de análisis y diseño menos tiempo invertirá en la fase de verificación y depuración. Por último, se debe realizar la importante fase de documentación del programa, con objeto de que cualquier persona ajena al mismo pueda entender qué hace y cómo lo hace. Antes de conocer las tareas a realizar en cada fase, vamos a considerar el concepto y significado de la palabra algoritmo. Un algoritmo es un método para resolver un problema mediante la combinación de una serie de pasos precisos, definidos y finitos. Un algoritmo es preciso en el sentido que los pasos que lo componen deben realizarse en un determinado orden y no en otro. Que sea definido implica que si se ejecuta varias veces el mismo algoritmo sobre el mismo conjunto de datos de entrada, siempre se obtienen los mismos datos de salida, es decir, el algoritmo es unívoco. Finalmente, que sea finito implica que debe finalizar después de un número finito de pasos. Un algoritmo debe producir un resultado en un tiempo finito. Los métodos que utilizan algoritmos se denominan métodos algorítmicos, en oposición a los métodos que implican algún juicio o interpretación que se denominan métodos heurísticos. Los métodos

1 El Diccionario de la Real Academia Española toma entre los nuevos términos aceptados: implementar.

Page 6: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 6

algorítmicos se pueden implementar en computadoras; sin embargo, los procesos heurísticos no han sido convertidos fácilmente en las computadoras. En los últimos años las técnicas de inteligencia artificial han hecho posible la implementación del proceso heurístico en computadoras. Entre las herramientas de que dispone el programador para una representación gráfica de un algoritmo destacan los ordinogramas, diagramas de flujo de Nassi-Schneiderman y últimamente se utiliza con más frecuencia la herramienta del pseudocódigo. Esta última representación es la más utilizada en lenguajes de programación estructurados como Turbo Pascal.

ANÁLISIS DEL PROBLEMA El primer paso para encontrar la solución a un problema mediante una computadora es el análisis del problema con una definición del mismo lo más exacta posible. Esta fase requiere –normalmente- el máximo de imaginación y creatividad por parte del programador. Dado que se busca una solución se debe examinar cuidadosamente el problema a fin de identificar qué tipo de información se necesita producir. A continuación el programador debe identificar aquellos elementos de información dados en el problema que puedan ser útiles para obtener la solución. Finalmente, un procedimiento para producir los resultados deseados a partir de los datos, y que será el algoritmo. Esta fase es muy importante y no debe tomarse a la ligera. Si no conoce exactamente lo que desea como salida del programa, puede producirle sorpresa lo que su programa realice, por ejemplo si el programa es para el cálculo de un interés bancario, debe conocer no sólo la tasa de interés y el capital, sino también el período de tiempo de depósito del capital.

DISEÑO Y VERIFICACIÓN DE ALGORITMOS En la etapa de análisis del proceso de programación se determina qué hace el programa. En la etapa de diseño se determina cómo hace el programa la tarea solicitada. Los métodos más eficaces para el proceso de diseño se basan en el conocido por divide y vencerás. Es

Análisis del problema

Definicón del problema

Datos de salida o

resultados

Datos de entrada

Page 7: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 7

decir, la resolución de un problema complejo se realiza dividiendo el problema en subproblemas y a continuación dividir estos subproblemas en otros de nivel más bajo, hasta que pueda ser implementada una solución en la computadora. Este método se conoce como diseño descendente, top-down o modular. El proceso de romper el problema en cada etapa y expresar cada paso en forma más detallada se denomina refinamiento sucesivo. Cada subproblema es resuelto mediante un módulo (subprograma) que tiene un solo punto de entrada y un solo punto de salida. Cualquier programa bien diseñado consta de un programa principal o módulo principal (el módulo de nivel más alto) que llama a subprogramas (módulos de nivel más bajo) que a su vez pueden llamar a otros subprogramas. Los programas estructurados de esta forma se dice que tienen un diseño modular y el método de fragmentar el programa en módulos más pequeños se llama programación modular. Los módulos pueden ser planeados, codificados, comprobados y depurados independientemente (incluso por diferentes programadores) y a continuación combinarlos entre sí. El proceso implica la ejecución de los siguientes pasos hasta que el programa termina: 1. Programar un módulo 2. Comprobar el módulo 3. Si es necesario, depurar el módulo 4. Combinar el módulo con los módulos anteriores El proceso que convierte los resultados del análisis del problema en un diseño modular con refinamientos sucesivos que permitan una posterior traducción a un lenguaje se denomina diseño del algoritmo. El diseño del algoritmo es independiente del lenguaje de programación en el que se vaya a codificar posteriormente. Considerando nuevamente el problema del interés bancario. Este problema se puede dividir en tres subproblemas: 1. Entrada de datos 2. Cálculo del interés (proceso) 3. Salida de resultados

Módulo principal

Submódulo 1 Submódulo 3 Submódulo 2

Page 8: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 8

A partir de este momento el problema ha quedado reducido a tres subproblemas más simples. La solución de cada uno de estos subproblemas es un refinamiento del problema principal.

VERIFICACIÓN DE ALGORITMOS Una vez escrito el algoritmo es necesario asegurarse de que éste realiza las tareas para las que ha sido diseñado, y que, por lo tanto, produce el resultado correcto y esperado. El modo más normal de comprobar un algoritmo es mediante su ejecución manual usando datos significativos que abarquen todo el posible rango de valores y anotando en una hoja de papel los valores que van tomando en las diferentes fases, los datos de entrada o auxiliares y, por último, los valores de los resultados. Este proceso se conoce como prueba del algoritmo o prueba de escritorio.

CODIFICACIÓN Codificar es escribir en lenguaje de programación de alto nivel la representación del algoritmo desarrollada en las etapas precedentes. Dado que el diseño de un algoritmo es independiente del lenguaje de programación utilizado para su implementación, el código puede ser escrito con igual facilidad en un lenguaje u otro. Para realizar la conversión del algoritmo en programa se deben sustituir las palabras reservadas en castellano por sus homónimos en inglés, y las operaciones e instrucciones indicadas en lenguaje natural expresarlas en el lenguaje de programación correspondiente.

DOCUMENTACIÓN La documentación de un programa se clasifica en interna y externa. La documentación interna se incluye en el código del programa fuente mediante comentarios que ayudan a la comprensión del código. El programa no necesita para su funcionamiento de la existencia de comentarios, es más, los comentarios no generan código ejecutable cuando son traducidos a código máquina. Los comentarios sólo sirven para hacer los programas más fáciles de leer y comprender, sobre todo para aquellas personas ajenas al mismo, e incluso para los mismos programadores. El objetivo del programador es escribir códigos sencillos y limpios. Debido a que las máquinas actuales soportan grandes memorias no es necesario recurrir a técnicas de ahorro de memoria, por lo que es recomendable incluya el mayor número posible de comentarios, y fundamentalmente que sean significativos. La documentación interna también se complementa con la utilización de identificadores2 significativos que indiquen o se adecuen más a la finalidad de los distintos componentes dentro del programa.

2 Un identificador es una combinación de caracteres alfabéticos y dígitos que se utiliza para poner nombre a los distintos componentes del programa (variables, constantes, funciones, tipos de datos, etc.).

Page 9: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 9

La importancia de una adecuada documentación interna se manifiesta todavía en mayor grado cuando los programas son complejos y tienen varios cientos de líneas y es muy difícil su seguimiento, incluso por el programador si ha pasado mucho tiempo desde que lo codificó. La documentación es vital cuando se desean corregir posibles errores o bien modificar el programa. Tales cambios se denominan mantenimiento del programa. Después de cada cambio la documentación debe ser actualizada para facilitar cambios posteriores. Es práctica corriente numerar las sucesivas versiones del programa así como indicar el nombre de los sucesivos programadores que han intervenido tanto en la concepción del programa inicial como las distintas modificaciones posteriores. Asimismo, es conveniente una adecuada presentación e indentación3 en las distintas líneas del programa, así como líneas en blanco que separen los distintos módulos, de forma que éste sea más legible. Hay que tener en cuenta que para el programa traductor el código fuente es un archivo de texto, es decir, una sucesión de caracteres y que la traducción a código ejecutable es independiente de su presentación y formato, pero no así para la persona que tiene que leer el código o modificarlo. Así, por ejemplo, el código: Program Misterio; var r, v: real; begin Write (‘Introduzca dato: ‘); Readln (r); v:= 4.1888 * r * r * r; Writeln (‘ Respuesta = ‘, v) end. Se transforma en el mismo código ejecutable pero es difícilmente legible y provoca mayor fatiga en su seguimiento y comprensión. La documentación externa debe incluir: 1. Listado del programa fuente, incluyendo mapas de memoria, referencias cruzadas y

cualquier otra cosa que se obtenga en el proceso de compilación 2. Explicación de cualquier fórmula o cálculos y expresiones complejas en el programa 3. Especificación de datos y formatos de pantalla para entrada y salida de los mismos, así

como cuantas consideraciones se estimen oportunas para mejorar la eficiencia del programa.

En general la documentación externa se compone de un manual de usuario y un manual de mantenimiento, sobre todo en grandes aplicaciones. En un proyecto grande cada uno de estos manuales abarca varios volúmenes que es necesario leer para conocer el origen, desarrollo y evolución de los distintos componentes del mismo.

COMPILACIÓN Y EJECUCIÓN Una vez que el algoritmo se ha convertido en programa fuente mediante el proceso de codificación, es preciso traducirlo a código o lenguaje máquina, único que la computadora

3 con indentación nos referimos a la sangría o margen que dejamos al codificar el programa y que permite que este sea más entendible.

Page 10: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 10

es capaz de entender y ejecutar. El encargado de realizar esta función es un programa traductor (compilador o intérprete). Si tras la compilación se presentan errores de compilación es preciso modificar la codificación del programa de forma que ésta se adapte a las reglas de sintaxis del lenguaje elegido, con el fin de corregir los errores. Este proceso continúa hasta que ya no se producen errores, con lo que el compilador proporciona el programa objeto, aún no ejecutable directamente. A continuación se procede al montaje o enlace (link) y producir finalmente el programa ejecutable. Una vez obtenido el programa ejecutable se pone en funcionamiento con sólo teclear su nombre (en el caso de DOS) o un simple clickeo sobre su nombre (en el caso del explorador de Windows). Suponiendo que no existen errores durante su ejecución (llamados errores en tiempo de ejecución4) se obtendrá la salida de los resultados del programa. Las instrucciones u órdenes para compilar y ejecutar un programa pueden variar según el tipo de compilador. Así, por ejemplo, Turbo Pascal compila y ejecuta con una sola orden, mientras que otros compiladores clásicos de Pascal siguen un proceso similar a lo expuesto anteriormente: compilar, enlazar y ejecutar.

VERIFICACIÓN La verificación y depuración son procesos sucesivos mediante los que se comprueba un programa con una amplia variedad de datos de entrada, llamados datos o test de prueba, que determinan si el programa tiene errores. Para realizar la verificación y depuración se debe desarrollar una amplia gama de datos de test: valores normales de entrada, valores extremos, otros con salida o resultado conocido y que comprueban aspectos esenciales del programa. Los errores más difíciles de detectar, y por tanto los más peligrosos, son los errores lógicos. Estos se producen por un mal diseño en la lógica del algoritmo, no producen errores de compilación ni de ejecución, y sólo pueden advertirse por la obtención de resultados incorrectos –cuando se conoce el resultado exacto de los cálculos o se tiene idea del orden de magnitud de los mismos-, por lo que resulta de una importancia crucial esta fase en la vida de un programa, antes de pasarlo definitivamente a una fase de explotación. Una vez detectados estos errores hay que volver a rediseñar el algoritmo, codificar y compilarlo de nuevo, para obtener el código ejecutable correcto.

TIPOS DE DATOS Los diferentes objetos de información con los que un programa Pascal trabaja se conocen en conjunto como datos. Todos las datos tienen un tipo asociado a ellos. Un dato puede ser un simple caracter, tal como 's', un valor entero tal como 70 o un número real tal como 312,53. Una operación de suma no tiene sentido con caracteres, sólo con números. Por consiguiente, si el compilador detecta una operación de suma de dos caracteres,

4 Intento de división por cero, raíces cuadradas de números negativos, intentos de apertura de archivos inexistentes, dispositivos periféricos no conectados, ...

Page 11: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 11

normalmente producirá un error, incluso entre tipos numéricos diferentes. La operación de suma se almacena de modo distinto en cada caso, ya que los números enteros y reales se almacenan de modo diferentes. A menos que el programa conozca el tipo de datos, si es un valor entero a real, no puede ejecutar correctamente la operación de suma. La asignación de tipos a los datos tiene dos objetivos principales:

1. Detectar errores de operaciones en programas 2. Determinar cómo ejecutar las operaciones

Los datos utilizados deben tener sus tipos declarados explícitamente ya que el lenguaje limita la mezcla de tipos en las expresiones. Pascal detecta muchos errores de programación antes que el programa se ejecute. El tipo de un dato determina la naturaleza del conjunto de valores que puede tomar una variable. Otro concepto importante a tener en cuenta es la representación interna de los números, o al menos el espacio de memoria ocupado por una variable de un tipo dado. La unidad de medida de la capacidad de memoria es el byte (octeto): un byte se compone de ocho cifras binarias (bits) que pueden tomar cada una el valor 0 ó 1.

TIPOS DE ENTEROS

TIPO RANGO Byte 0 .. 255 Integer -32768 .. 32767 Longint -247483648 .. 24748367 Shortint -128 .. 127 Word 0 .. 65535

Page 12: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 12

TIPOS DE REALES

TIPO RANGO CIFRAS Real 2.9x10-39 .. 1.7x1038 11-12 Single 1.5x10-45 .. 3.4x1038 7-8 Double 5.0x10-324 .. 1.7x10308 15-16 Extended 1.9x10-4932 .. 1.1x104932 19-20 Comp -(263 +1) .. 263 +1 19-20

TIPOS CARACTER (CHAR) El tipo char es un tipo de datos que puede contener un solo caracter y cada uno de estos caracteres puede ser expresado gracias al código ASCII ampliado. Ejemplo: ‘A’ ‘a’ ‘b’ ‘*’ ‘5’ ‘ ‘ Se le puede asignar a una constante un carácter por medio de su código:

equivale a

#65 chr(65) se traduce en A #26 o ^Z cierre del archivo

#27 tecla ESC #13 tecla ENTER

TIPOS LÓGICOS (BOOLEAN) El tipo lógico (boolean) es al igual que el tipo caracter, parte del ISO Pascal estándar. El tipo lógico puede tomar sólo dos valores posibles: true (verdadero) y false (falso). Al igual que el tipo char, el tipo boolean es un tipo ordinal, lo que significa que tiene un número fijo de valores posibles que existen en un orden definido. Una variable lógica ocupa sólo un byte en memoria. Los valores lógicos son de tipo ordinal, y sus relaciones son: false < true

TIPOS DE DATOS DEFINIDOS POR EL USUARIO Todos los tipos de datos estudiados hasta ahora son de tipo simple, predefinidos por Turbo y listos para utilizar. Sin embargo, uno de los aspectos más potentes de Turbo Pascal es su

Page 13: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 13

capacidad para crear estructuras de datos a partir de estos datos simples. Los datos estructurados aumentan la legibilidad de los programas y simplifican su mantenimiento. Los tipos de datos definidos por el usuario se clasifican en: - Escalares definidos por el usuario (enumerado y subrango) - Registros - Arrays - Conjunto - Archivo - Puntero - Procedimiento

TIPO CADENA (STRING) Un tipo string (cadena) es una secuencia de caracteres, pudiendo ser cero o más caracteres correspondientes al código ASCII, escrito en una línea sobre el programa y encerrada entre apóstrofos. El tratamiento de cadenas es una característica muy potente de Turbo Pascal que no tiene el ISO Pascal estándar, aunque tiene mucha similitud con su tipo packed array. Ejemplos: 'Turbo' 'Estás de acuerdo' #13#10 ‘,’ - Una cadena sin nada entre los apóstrofos se llama cadena nula o cadena vacía. - La longitud de una cadena es el número de caracteres encerrados entre los apóstrofos.

CONSTANTES Una constante es un valor que no puede cambiar durante la ejecución del programa, recibe un valor en el momento de la compilación del programa y este valor no puede ser modificado. Las constantes pueden ser: · constantes literales · constantes con nombres o declaradas · constantes de tipos (tipeadas) Las constantes deben ser declaradas antes de su utilización y pueden ser enteras o reales, caracteres o cadenas de caracteres, conjuntos o arrays, e inclusive de tipo enumerado.

& Constantes literales

Page 14: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 14

Una constante literal es un valor de cualquier tipo que se utiliza como tal. VolumenEsfera := 4/3 * Pi * Radio * Radio * Radio 4 y 3 son constantes literales de valores 4 y 3.

& Constantes con nombres Son constantes que se identifican por un nombre y el valor asignado. Formato: const

identificador = valor; Ejemplos: const

Pi = 3.141592; DosPi = 2 * Pi; caracter = 'B'; cuenta = 32;

En Pascal estándar, la declaración de constantes se sitúa inmediatamente después de la cabecera Program. En Turbo Pascal no es obligatoria la situación anterior, pero sí recomendable.

VARIABLES Las variables son objetos cuyo valor puede cambiar durante la ejecución del programa. El cambio se produce mediante sentencias ejecutables. - Todas las variables de un programa Pascal deben ser declaradas antes de ser usadas. Declaraciones: var

variable1: tipo1; variable2: tipo2; ......................... ......................... variableN: tipoN;

Ejemplos:

NumeroEmpleado : Integer; { número de empleado } Horas : real; { horas trabajadas } Tasas : real; { tasa horaria } Edad : Integer; { edad del empleado } Apellidos : string [30]; { apellidos del empleado } Letra1, Letra2, Letra3 : char;

Page 15: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 15

Num1, Num2 : integer; - Es una buena práctica de programación utilizar nombres de variables significativas que

sugieren lo que ellas representan, ya que esto hace al programa más legible y fácil de comprender, también es buena práctica incluir breves comentarios que indiquen cómo se utiliza la variable.

SENTENCIAS Las sentencias describen las acciones algorítmicas que deben ser ejecutadas En general las sentencias se clasifican en: ejecutables, que son las que especifican operaciones de cálculos aritméticos y entradas / salidas de datos, y en no ejecutables que no realizan acciones concretas, pero ayudan a la legibilidad del programa y no afectan a la ejecución del mismo. Las sentencias ejecutables aparecen en el cuerpo del programa a continuación de la palabra reservada Begin

& LA SENTENCIA DE ASIGNACION

La sentencia de asignación se utiliza para asignar (almacenar) valores o variables. La asignación es una operación que sitúa un valor determinado en una posición de la memoria. La operación de asignación se demuestra en pseudocódigo con el símbolo “←”, para denotar que el valor situado a su derecha se almacena en la variable situada a la izquierda. Formato: Variable ← expresión Variable es un identificador válido declarado anteriormente. expresión es una variable, constante o una expresión o fórmula a evaluar En Pascal el operador “←” se sustituye por el símbolo:= , que se denomina operador de asignación Variable := expresión El valor de expresión se asigna a la variable. Cuidado!!! El tipo de expresión debe ser del mismo tipo que el de la variable.

Existen algunas asignaciones un tanto diferentes de las convencionales, estas corresponden al uso del contador y del acumulador. Contador: Un contador es una variable que se incrementa, cuando se ejecuta, en una unidad o en una cantidad constante. Pepe := 9 Pepe := Pepe + 1;

Page 16: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 16

Acumulador: Un acumulador es una variable que se incrementa en una cantidad variable. Acum := Acum + cant; cant es una variable Si Acum = 15 Cant = 20 Entonces ahora Acum valdría 35

EXPRESIONES Y OPERACIONES ARITMÉTICAS Las variables y constantes estudiadas anteriormente se pueden procesar utilizando operaciones y funciones adecuadas a sus tipos: En el párrafo siguiente, se examinarán las expresiones y operaciones que se utilizan con datos numéricos.

& Operadores aritméticos: +, -, *, / Los operadores aritméticos +, -, * pueden ser utilizados con tipos enteros o reales. Si ambos son enteros, el resultado es entero; pero si alguno de ellos es real, el resultado es real. La operación / devuelve un resultado real. 2+3 = 5 2+3.0 = 5.0

& Operadores aritméticos div y mod Solo se pueden utilizar con enteros, siendo la salida otro entero. Div devuelve el valor de la división entera y Mod da el resto de la división entera.

13 DIV 2 = 6 13 MOD 2 = 1

& Reglas de las expresiones o prioridades: se respeta las mismas prioridades del álgebra.

OPERACIONES DE ENTRADA / SALIDA Los datos se pueden almacenar en la memoria de tres formas diferentes: asociados con constantes, asignados a una variable con una sentencia de asignación o una sentencia de lectura. (Ya se vieron las dos primeras formas) La tercera forma es la sentencia de lectura, que es la más indicada si se desea manipular datos diferentes cada vez que se ejecute el problema. Además, la lectura de los datos permite asignar valores desde dispositivos y de archivos externos (por ejemplo, un teclado o una unidad de disco), denominándose operación de entrada o de lectura.

Page 17: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 17

A medida que se realizan los cálculos en un programa, se necesitan visualizar los resultados Esta operación se conoce como operación de salida o de escritura. En los algoritmos las instrucciones de entrada / salida escritas en pseudocódigo son: leer (listas de variables entrada) leer (v. z, x) escribir (listas de variables salida) escribir (a, b, c) En Pascal todas las operaciones de entrada / salida se realizan ejecutando unidades de programa especiales denominadas procedimientos de entrada / salida que forman parte del compilador Pascal y sus nombres son identificadores estándar:

Procedimientos de entrada Read o ReadLn Procedimientos de salida Write o WriteLn

& La escritura de resultados (salida) Para ser útiles, los programas deben proporcionar información de la salida (resultados). Esta salida toma información de la memoria y la sitúa (almacena) en: la pantalla, en un dispositivo de almacenamiento (disco duro o flexible), o en un puerto de E/S (puertos serie para comunicaciones o impresoras, normalmente paralelos). Procedimiento WriteLn El propósito de WriteLn es escribir (visualizar) información en la pantalla Formato: WriteLn (ítem, ítem..); ítem es el objeto que desea visualizar: un valor literal (entero, real, un carácter una cadena, o un valor lógico (True o False), una constante con nombre, una variable, o una llamada a una función. Cuando se ejecuta el procedimiento WriteLn, se visualizan todos los elementos en el orden dado y en la misma línea. Al terminar de visualizar toda la línea, el cursor avanza (salta) al comienzo de la línea siguiente. Procedimiento Write Como se ha dicho, después de ejecutar el procedimiento WriteLn, el cursor avanza (salta) al comienzo de la siguiente línea. Si se desea que el cursor quede en la misma línea se debe utilizar el procedimiento Write. Formatos de salida Turbo Pascal permiten controlar, en cierta medida las instrucciones de salida que presentan resultados, ya que es posible especificar el número de posiciones del campo de escritura y para los números reales es posible precisar el número de decimales deseado. Se pueden utilizar especificadores de formato de campo para definir dicho ancho.

x := 265.7892 WriteLn(x :10 :4); 265.7892 WriteLn(x :10 :2); 265.79 WriteLn(x :6 :4); ********* X := 14;

Page 18: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 18

WriteLn(x :4); 14 X := ‘AB’ WriteLn(x :4); _ _ AB

Lo anteriormente expresado es válido para el proceso WRITE Impresión de resultados (salidas a impresora) Las salidas a pantalla se obtienen mediante los procedimientos Write y WriteLn. Si se desea enviar resultados a otro dispositivo, es preciso especificar el nombre del archivo como primer argumento de las instrucciones Write y WriteLn. Para poder realizar la operación de enviar salidas a la impresora, en lugar de a la pantalla, se necesita la unidad Printer. Printer define un archivo llamado lst y asocia este archivo al puerto de comunicaciones LPT1 (impresora) del DOS. Se pueden enviar datos a la impresora, incluyendo lst en las instrucciones Write y WriteLn. Es preciso, sin embargo, definir previamente en la sección uses la unidad printer. Ejemplo:

Uses Printer var ......................... begin

......................... Write (Lst, 'el .......................... ) WriteLn (Lst, 'pl............ ) .........................

end. Este programa imprime en la impresora:

& La entrada de datos (lectura) Los datos que se pueden leer son: enteros, reales, caracteres o cadenas. No se puede leer un boolean o un elemento de tipo enumerado. Los datos estructurados, arrays, registros o conjuntos, no se pueden leer globalmente y se suele recurrir a diseñar procedimientos específicos. Los procedimientos de lectura son Read y ReadLn. Formato:

Read (var1, var2, ...); ReadLn (var2, var2, ...);

var es igual que para Write

Page 19: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 19

La entrada de datos desde el teclado se hace de a un valor cada vez y las instrucciones ReadLn y Read esperan hasta que se pulsa la tecla ENTER, antes de asignar un valor a la variable. Ejemplo:

ReadLn (edad); ReadLn (cantidad); Read (interes);

El usuario debe introducir los datos de entrada en el orden en que aparecen en las instrucciones Read. Diferencias entre Read y ReadLn En Read, después de pulsar la tecla ENTER, el cursor permanece inmediatamente después del último caracter introducido. En ReadLn, el cursor se envía al principio de la siguiente línea, tras pulsar la tecla ENTER. Otra diferencia importante surge del siguiente análisis: supongamos que se quieren leer desde teclado una serie de números enteros y reales, mediante las sentencias: readln(int1, re1); readln(int2); donde int1 es una variable tipo entero (integer), y re1 es una variable tipo real (real). Y supongamos que el usuario ingresa los siguientes datos: 73 98.45 62 ↵ ( ↵ significa ENTER) 45 ↵ Cuando la computadora lee 73, lo coloca en la variable int1, luego lee el 98.45 (todo en la misma línea, separados por un espacio) y lo coloca en re1. Pero como se trata de un readln (read line) ignora todo el resto que esté en la misma línea (en este caso ignora al 62), y hace un avance de carro (es decir, pasa a la línea siguiente). Al leer la línea siguiente coloca al 45 en int2. El resultado es: int1 = 73 re1 = 98.45 int2 = 45 Ahora supongamos que tenemos los mismos datos de entrada, pero con las siguientes sentencias: read(int1, re1); readln(int2); En este caso lee el 73 y lo coloca en int1, luego lee el 98.45 y lo coloca en re1. Como se trata de un read, no hace avance de línea, y pasa a ejecutar la sentencia siguiente (o sea el readln). Al ejecutar el readln siguiente carga en int2 al número 62, porque el read anterior

Page 20: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 20

no había hecho avance de línea, y por consiguiente el número 45 no se almacena en ninguna variable. El resultado es:

int1 = 73 re1 = 98.45 int2 = 62 Así es fácil observar la diferencia entre read y readln.

OTRAS OPERACIONES BÁSICAS Clrscr

Implica borrar toda la pantalla. Clrscr pertenece a la unidad Crt. La orden (procedimiento) ClrScr borra (limpia) la pantalla de la ventana actual y sitúa el cursor en la esquina superior izquierda. Turbo Pascal considera las coordenadas de la esquina superior izquierda 1,1. Para poder utilizar Clrscr, se deberá declarar en la cláusula uses la unidad Crt.

GotoXY

Movimiento del cursor La orden (procedimiento) GotoXY mueve el cursor a la posición x, y, donde x es la columna (contando de izquierda a derecha) e y es la fila (contando de arriba hacia abajo). GotoXY (x, y) La esquina superior izquierda es 1.1. GotoXY requiere el uso de la unidad Crt..

ESTRUCTURAS DE SELECCIÓN

En la programación estructurada se utilizan sólo tres estructuras de control básicas: Ø El bloque de instrucciones consecutivas o secuenciales Ø La instrucción condicional o selectiva

1. La sentencia SI-ENTONCES (if-then) 2. La sentencia según

Page 21: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 21

Ø El bucle o estructura repetitiva 1. La instrucción PARA (for) 2. La instrucción MIENTRAS (while) 3. La instrucción REPETIR (Repeat)

& CONSECUTIVAS o SECUENCIALES Todo lo explicado hasta el momento viene a referencia del tipo de estructuras consecutivas o secuenciales, por lo que no trataré nuevamente el tema.

& SELECTIVAS Las estructuras selectivas se utilizan para tomar decisiones lógicas; de ahí que se suelan denominar estructuras de decisión o alternativas. Son dos: la sentencia if y la sentencia case. If Esta sentencia, es considerada de alternativa doble (si se cumple cierta condición entonces ... , sino .... / If ...... then ...... else..... ). if <condición>

then < acción S1> else < acción S2>

Cuidado!!! Cuando alguna de las opciones tiene más de una instrucción, dicha opción deberá llevar un begin con su correspondiente end. Debe notarse que el end del then no tiene puntuación y en cambio el del else si lleva punto y coma (;). Esto se debe que este end es el último del if. Otro caso para tener en cuenta es cuando la variable por la que se consulta es del tipo Boolean (sólo puede valer True o False). En este caso no es necesario especificar en la pregunta si la variable es verdadera, ya que Pascal lo toma como predeterminado.

if a then Writeln ('Afirmativo') else Writeln ('Negativo');

Case

Case <variable> of Opción1 < acción 1>

Opción2 < acción 2> Opción n < acción n>

else < acción m>

Case A of 1 Imprimir “UNO”

2 Imprimir “DOS”

Page 22: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 22

3 Imprimir “TRES” 4 Imprimir “CUATRO” else Imprimir “CINCO” El código de la estructura es:

case A of 1 : writeln ( 'U n o '); 2 : writeln ( 'D o s '); 3 : writeln ( 'T r e s '); 4 : writeln ( 'C u a t r o ');

else writeln ( 'C i n c o '); end;

& REPETITIVAS Las estructuras repetitivas (llamadas también bucles, lazos o ciclos) se utilizan para realizar varias veces el mismo conjunto de operaciones. Dentro de ellas se encuentran aquellas donde la cantidad repeticiones se manejan por un número y las que se realizan hasta que se cumple cierta condición. Básicamente tenemos tres sentencias: for, while y la sentencia repeat-until For Esta sentencia, es un bucle controlado por un contador, denominado variable de control o índice. for variable cont = inicial to final do

begin < acción 1> < acción 2> ......................

end While En este ciclo la pregunta de condición se hace cada vez antes de comenzar el ciclo. Ejemplo: Se pide un programa donde se ingresan una serie de números positivos, el último es un valor negativo. Se pide calcular el promedio de los valores positivos (no incluir el último valor), e imprimir los valores y su promedio. El código del procedimiento en su parte central, dice:

Page 23: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 23

S := 0; N := 0; write ( 'Ingrese un valor '); read (A); writeln ( 'El valor es : ',A ); WHILE A >= 0 DO begin S := S + A; N := N + 1; write ( 'Ingrese un valor '); read (A); writeln ( 'El valor es : ',A ); end Luego el programa deberá calcular el promedio ayudándose para ello de S (acumulador) y de N (contador). Repeat Esta solución se utiliza cuando el ciclo debe incluir el último valor ingresado, ya que la pregunta se hace al final del ciclo. Ejemplo Se ingresan una serie de números distintos, el último es el valor 12. Se pide un programa que calcule el promedio de los valores (incluir el último valor), e imprimir los valores y su promedio. El código del procedimiento es en su parte central es:

S := 0; N := 0; repeat write ( 'Ingrese un valor '); read (A); writeln ( 'El valor es : ',A ); writeln ; S := S + A; N := N + 1; until A = 12;

Luego el programa deberá calcular el promedio ayudándose para ello de S (acumulador) y de N (contador). Como se puede observar esta estructura carece de begin y end, ya que los dos extremos están definidos por el propio ciclo.

Page 24: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 24

SINTESIS

Estructura de un programa Pascal:

• Encabezamiento del programa • Declaraciones • Bloque del programa

* ENCABEZAMIENTO * BLOQUE DE DECLARACIONES: - Declaración de rótulos - Declaración de constantes - Declaración de tipos - Declaración de variables - Declaración de subprogramas * BLOQUE DEL PROGRAMA: Begin <acción 1>; <acción 2>; . <acción n> End. El encabezamiento del programa será de la forma: PROGRAM <nombre> (<archivos utilizados>); ACCIONES: * DE SECUENCIA: - asignación - invocación a procedimientos (Read, Write) - sentencia with * DE SELECCIÓN: - con una opción: If <exp.lógica> then <acción>; - con dos opciones: If <exp.lógica> then <acción1> else <acción2>;

Page 25: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 25

- con varias opciones: Case <expresión de control> of <valor1> : <acción1>; <valor2> : <acción2> End; * DE REPETICIÓN: - con contador: For <vble.ctrl.> := <exp.1> to <exp.2> do <acción> For <v.c.> := <e.1> downto <e.2> do <acc.> - ciclo con condición a la entrada: While <exp. lógica> do <acción>; - ciclo con condición a la salida: Repeat <acción 1>; <acción 2> until <exp. lógica>;

Page 26: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO II

Procedimientos y Funciones

Page 27: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 27

PROCEDIMIENTOS Y FUNCIONES

Los programas Pascal pueden escribirse en forma modular, permitiendo así que un problema general se descomponga en una secuencia de subproblemas individuales. La modularización proporciona dos importantes ventajas: 1) Para tareas que deben efectuarse más de una vez puede definirse un módulo que sea escrito una vez y pueda ser llamado desde distintos puntos del programa. 2) La claridad lógica resultante de la descomposición de un programa en módulos concisos e individuales, donde cada módulo representa una parte bien definida del problema total. Hay dos tipos de módulos: PROCEDIMIENTOS y FUNCIONES. Estos dos tipos de estructuras son similares. Sin embargo, se invocan de diferente forma e intercambian información de forma diferente también. Hay procedimientos estándar: read y write. Hay funciones estándar: sqr.

PROCEDIMIENTOS Es una estructura de programa autónoma incluida dentro de un programa. Llamada en otros lenguajes subrutina. Se lo invoca con su nombre seguido de una lista opcional de parámetros. Los parámetros van entre paréntesis y si hubiera más de uno, separados por comas. Cuando se invoca un procedimiento, el control se transfiere automáticamente al comienzo del mismo. Se efectúan entonces las sentencias ejecutables del procedimiento, teniendo en cuenta cualesquiera declaraciones especiales propias del procedimiento. Cuando se han ejecutado todas las acciones se devuelve el control automáticamente a la sentencia inmediatamente posterior a la referencia al procedimiento. Ejemplo: Un programa lee tres cantidades enteras y determina luego qué cantidad es la mayor. PROGRAM PRINCIPAL (Input, Output); Var a,b,c: integer; Procedure Maximo; Var max: integer; Begin If a > b then max:= a else max := b; If c > max then max := c; writeln (“El máximo es:”, max) End;

Page 28: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 28

Begin * principal * readln (a,b,c); while a<> 0 do Begin Maximo; readln (a,b,c) End End. Observación: Las variables a, b y c, definidas en la parte principal del programa, son reconocidas en todo el programa, incluyendo el procedimiento. Pero max, está definida dentro del procedimiento, por lo que no es posible hacer uso de la variable max dentro de la parte principal del programa. Ámbito de los identificadores Aquellas constantes y variables definidas dentro del bloque que contiene la declaración de procedimiento pueden ser utilizadas en cualquier punto dentro de este bloque, ya sea interior o exteriormente al procedimiento. Los identificadores definidos de esta manera se consideran globales al procedimiento. Las constantes y variables locales no están definidas fuera del procedimiento y no pueden ser utilizadas externamente. El ámbito de un identificador se refiere a la región dentro de la cual está declarado el identificador y dentro de la cual puede ser utilizado. Este concepto se aplica a todos los tipos de declaraciones, no sólo a constantes y variables. En el PROGRAM PRINCIPAL que incluye al PROCEDURE MAXIMO las variables a, b y c están declaradas fuera del procedimiento y por lo tanto son globales a él. Estas variables pueden utilizarse tanto fuera como dentro de él. Sin embargo la variable Max está declarada dentro del procedimiento y por ello es local a él, no pudiendo utilizarse en otra parte. En general son preferibles los identificadores locales a los globales, siempre que esto no sea inconsistente con la lógica general del programa. El uso de identificadores locales contribuye a una mejor legibilidad del programa y minimiza la probabilidad de errores por referencias incorrectas. Es posible utilizar -aunque no es buena práctica de programación- utilizar un mismo identificador para representar entidades diferentes en partes diferentes del programa. Así, un identificador que se declare localmente dentro de un procedimiento puede tener el mismo nombre que un identificador global declarado externamente. En tales situaciones, la definición local tiene precedencia dentro de su ámbito. Fuera de su ámbito, el identificador local estará indefinido, por lo que se aplicará la definición global. A continuación se muestra la estructura esquemática de un programa Pascal que contiene dos procedimientos: PROGRAM Muestra (Input, Output); VAR a,b: integer; c,d: char;

Page 29: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 29

PROCEDURE Uno; Var a,d: real; BEGIN . END; PROCEDURE Dos; Var a: char; b: boolean; BEGIN . END; BEGIN (* programa principal - sentencias ejecutables*) . END. Obsérvese que varios de los nombres se usan para variables distintas en los procedimientos y en el bloque principal. En particular, a se redefine dentro de cada procedimiento, y b y d se redefinen cada una en un procedimiento. Las definiciones locales tendrán precedencia dentro de sus respectivos procedimientos. Dentro del bloque principal, sin embargo, las variables se interpretarán como en las declaraciones originales. Observe que el uso de las variables locales dentro de sus respectivos ámbitos no altera los valores que se asignan a las variables globales. El hecho de que las variables locales y globales compartan los mismos nombres no tiene ningún efecto. Parámetros Muchos programas precisan que se intercambie información entre un procedimiento y el punto en que el procedimiento fue invocado. Un modo de conseguir esto es emplear variables globales. El uso de parámetros ofrece un método mejor para el intercambio de información entre un procedimiento y su punto de referencia. Cada dato se transfiere entre un parámetro actual, incluido dentro de la referencia al procedimiento, y un parámetro formal correspondiente, definido dentro del propio procedimiento. Cuando se invoca un procedimiento, los parámetros actuales reemplazan a los parámetros formales, creando así un mecanismo de intercambio de información entre el procedimiento y su punto de referencia. La manera en que se intercambia la información depende, sin embargo, de la manera en que se definan y utilicen los parámetros. PROGRAM muestra (Input, Output); VAR a,b,c: real; PROCEDURE flash (x, y: real); BEGIN . (* procesar los valores de x e y *) .

Page 30: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 30

END; BEGIN (* sentencias ejecutables del programa principal *) . flash (a, b); . flash (c, d); . END. Las variables x e y son parámetros formales de tipo real definidos dentro del procedimiento flash. Los parámetros actuales son las variables a, b, c y d. Reglas de sustitución de parámetros formales por parámetros actuales: 1.- El número de parámetros actuales de la referencia debe ser igual al número de parámetros formales de la definición del procedimiento. 2.- Cada parámetro actual debe ser del mismo tipo que su correspondiente parámetro formal. 3.- Cada parámetro actual debe expresarse de modo que sea consistente con su correspondiente parámetro formal, según determine la clase del parámetro formal. Un procedimiento puede contener cuatro clases diferentes de parámetros formales. Son los: parámetros-valor, parámetro-variable, parámetro-procedimiento y parámetro-función. Parámetro - Valor: Son considerados parámetros de entrada a sus respectivos procedimientos. Implica una transferencia de valor más que una verdadera sustitución de parámetro. Cuando se transfiere información entre un parámetro actual y un parámetro-valor, el valor del parámetro actual se asigna al parámetro-valor. Este valor puede ser procesado entonces por el procedimiento (referenciando el parámetro-valor). Sin embargo los valores representados por parámetro-valor no pueden ser transferidos en dirección opuesta, esto es, desde el procedimiento al punto de referencia del programa. Es por esto que los parámetros-valor se consideran parámetros de entrada. Se declaran incluyendo simplemente el nombre y los tipos de datos correspondientes dentro de la cabecera del procedimiento, sin ningún prefijo (tal como VAR). La ausencia de tal prefijo es lo que identifica automáticamente a esta clase de parámetro. El PROCEDURE flash del PROGRAM muestra, hace uso de este tipo de parámetros. Debe quedar bien claro que cualquier alteración en el valor de un parámetro-valor dentro del procedimiento no afectará al valor de ninguno de los parámetros actuales (recuérdese que los parámetros-valor se consideran parámetros de entrada). Son fáciles de usar en situaciones que permitan una transferencia de información en un sólo sentido. Son convenientes para operar en situaciones donde la información se transfiere sólo desde la referencia al procedimiento. Parámetro - Variable:

Page 31: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 31

Se utilizan cuando la información debe ser transferida en ambas direcciones, entre el procedimiento y su referencia. En otras palabras, el procedimiento debe ser capaz de incorporar entrada y salida a y desde el bloque invocante. Cuando se invoca un procedimiento que contiene un parámetro-variable, el parámetro formal del procedimiento sustituye al parámetro actual de la referencia. Esto contrasta con el uso de un parámetro-valor, donde el valor del parámetro actual es asignado al parámetro formal (obsérvese la distinción entre asignación y sustitución). Este proceso de sustitución es el que permite la transferencia de información en ambos sentidos entre la referencia y al procedimiento y el propio procedimiento. Por otra parte, debe aclararse que sólo una variable puede sustituir a otra variable. Así los parámetros actuales que sustituyen a parámetros-variable deben ser variables; no pueden ser constantes o expresiones. Otra consecuencia del proceso de sustitución es el hecho de que cualquier cambio en el valor de un parámetro-variable dentro de un procedimiento modificará también el valor del correspondiente parámetro actual fuera del procedimiento. Por tanto, los parámetros-variable pueden afectar globalmente a un programa, aun cuando su ámbito sea local al procedimiento dentro del cual se declararon. Los parámetros-variable son declarados dentro de la cabecera del procedimiento, al igual que los parámetros-valor. Sin embargo, las declaraciones de parámetros-variable deben ir precedidas por la palabra clave VAR. PROGRAM muestra (Input, Output); VAR a, b: integer; c, d: real; PROCEDURE flash (VAR x: integer; VAR y: real); BEGIN (* procesar los valores de x e y *) . END; BEGIN (* sentencias ejecutables del programa principal *) flash (a, c); flash (b, d); END. La primera referencia hace que los parámetros actuales a y c sean sustituidos por x e y. Si el valor de x o y se altera dentro del procedimiento, se producirá la correspondiente alteración del valor de a o c en el bloque principal. De modo similar, la segunda referencia da lugar a que b y d sean sustituidos por x e y. Cualquier alteración de x o y dentro del procedimiento producirá por tanto la correspondiente alteración de los valores de b y d en el bloque principal. Observe que los parámetros actuales concuerdan en número y tipo con los correspondientes parámetros formales.

FUNCIONES

Page 32: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 32

Una función es una estructura autónoma de programa, similar en muchos aspectos a un procedimiento, sólo que una función se usa para devolver un sólo valor de tipo simple a su punto de referencia. Además, una función se referencia especificando su nombre dentro de una expresión como si fuera una variable ordinaria de tipo simple. El nombre de la función puede venir seguido por uno o más parámetros actuales encerrados entre paréntesis y separados por coma. En la mayoría de los casos, los parámetros actuales transferirán información a parámetros-valor dentro de la función y pueden, por tanto, expresarse como constantes, variables o expresiones. En general los parámetros formales serán parámetros-valor en vez de parámetros-variable. Esto permite que los correspondientes parámetros actuales se expresen como constantes, variables o expresiones. (Recuérdese que estos parámetros sólo proporcionan valores de entrada a la función; el único valor de salida suministrado por la función vendrá representado por el nombre de la función y no por un parámetro.) El bloque es similar al de un procedimiento y aplica las mismas reglas de ámbito que éste. Dentro del bloque, sin embargo, el identificador que representa el nombre de la función debe tener asignado un valor del tipo apropiado (según se especifica en la cabecera). Este es el valor que la función devuelve a su punto de referencia. Pueden asignarse valores al nombre de la función en dos o más puntos dentro del bloque. Sin embargo, una vez que se hace una asignación no puede alterarse posteriormente. He aquí el programa Pascal completo que determina el factorial de un valor de entrada dado: PROGRAM factorial (Input, Output); VAR x: integer; FUNCTION factorial (n : integer): integer; VAR factor, producto: integer; BEGIN IF n < = 1 THEN factorial := 1 ELSE BEGIN producto := 1; FOR factor := 2 TO n DO producto := producto * factor; factorial := producto END END; BEGIN (* bloque ejecutable del programa *) WRITE (“Introducir un entero positivo :”); READLN (X); WRITELN ( ¨ X!= ¨, factorial (X)) END. Obsérvese que la función factorial es llamada sólo una vez, en la última sentencia Writeln del bloque principal. Al ejecutarse el programa debe tenerse cuidado con el valor que se le

Page 33: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 33

asigne a X, ya que puede producirse un rebose si a X se le asigna un número demasiado grande. Analicemos esta otra versión, que en principio parece más atrayente que la original, pero no es válida, ya que altera el valor de factorial después de su asignación inicial, cuando n es mayor que 1. FUNCTION factorial (n : integer): integer; VAR factor: integer; BEGIN factorial := 1; IF n > 1 THEN FOR factor := 2 TO n DO factorial := factorial * factor END; Más sobre parámetros A veces es deseable que un procedimiento o función dado invoque a otro procedimiento o función que ha sido definido (declarado) fuera del ámbito de ese procedimiento o función. Podemos desear que un procedimiento A haga uso de la función B, aunque la función B haya sido definida fuera del procedimiento A. Esto puede conseguirse transfiriendo como parámetro el procedimiento o función externo (por ejemplo, la función B) al procedimiento o función dado (por ejemplo, el procedimiento A). Pascal admite parámetros-procedimiento y parámetros-función, además de parámetros-valor y parámetros-variable. PROGRAM principal (Input, Output); . . FUNCTION calc (w: integer): real; . BEGIN ( * función calc * ) . calc := ... END; ( * función calc * ) PROCEDURE proceso (FUNCTION f (u: integer): real; cl, c2: integer); VAR c : integer; x : real; BEGIN FOR c := c1 TO c2 DO BEGIN X:= F (c); writeln (“X =“, x) END END; ( * proceso * )

Page 34: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 34

BEGIN ( * bloque principal * ) . proceso (calc, 1, 100); . END.

Page 35: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 35

PROCEDIMIENTOS Y FUNCIONES (EJEMPLOS)

Ejercicio 1 Se dispone de un registro diario de lluvias de un mes en una localidad. Los registros, que se leerán por teclado, traerán el día del mes y la cantidad llovida, y vendrán ordenadamente por día. El primer registro a leer contendrá el número del mes y el año. Si llovió algún día del mes, indicar cuál fue el día más lluvioso e informar si llovió dos días seguidos en el mes. Si no llovió en todo el mes se debe emitir un mensaje en tal sentido. PROGRAM LLUVIAS; Type Tdias = 1..31; Var Llovio, Seguidos : Boolean; LluviaMax : Integer; DiasMes, DiaLluviaMax: Tdias; PROCEDURE CantDias; {determina la cantidad de días de un mes} Type Tmeses = 1..12; Tanios = 1901..2000; Var Mes: Tmeses; Anio : Tanios; Begin Readln (mes, anio); Case mes of 1,3,5,7,8,10,12: DiasMes := 31; 4,6,9,11: DiasMes := 30; 2: If (anio mod 4 = 0) then DiasMes := 29 else DiasMes := 28 End End; PROCEDURE Imprimir; Begin If Llovio then Begin Writeln (‘Día más lluvioso del mes:’, DiaLluviaMax, ‘Lluvia caida:’, LluviaMax); If Seguidos then Writeln (‘Llovió dos días seguidos’) End else Writeln (‘No llovió en todo el mes’) End;

Page 36: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 36

PROCEDURE ProcesarMes; Var Dia: Tdias; Lluvia, LluviaAnt: Integer; PROCEDURE ProcesarDia; Begin Readln (Dia, Lluvia); If Lluvia > 0 then Begin {llovió ese día} Llovio := True; If LluviaAnt > 0 then Seguidos := True; {también llovió el día anterior} If Lluvia > LluviaMax then Begin LluviaMax := Lluvia; DiaLluviaMax:= Dia End; End; LluviaAnt := Lluvia End; Begin {ProcesarMes} LluviaMax := 0; LluviaAnt := 0; Llovio := False; Seguidos := False; Repeat ProcesarDia until Dia = DiasMes; End; {ProcesarMes} Begin {Lluvias} CantDias; ProcesarMes; Imprimir End. {Lluvias} Ejercicio 2 Dado un telegrama terminado en punto, contar cuántas veces aparece cada letra minúscula, exceptuando la ‘ñ’. PROGRAM Minusculas;

Page 37: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 37

Type Tvector = array [‘a’ .. ’z’] of Integer; Var Cantidad : Tvector; PROCEDURE Inicializar; Var C : char; Begin For C := ‘a’ to ‘z’ do Cantidad [C] := 0 End; PROCEDURE ArmarTabla; Var C : char; Begin Read (C); While C <> ‘.’ do Begin If C in [‘a’ .. ‘z’] then Inc (Cantidad[C]); Read (C) End End; PROCEDURE Imprimir; Var C : char; Begin Writeln (‘Letra ’ , ‘ Cantidad de apariciones’); For C := ‘a’ to ‘z’ do Writeln (C, ‘ ‘: 20, Cantidad[C]: 3) End; Begin Inicializar; ArmarTabla; Imprimir End. Ejercicio 3 Escribir un algoritmo que contenga una función que calcule la suma de los dígitos de la representación decimal de un valor entero no negativo. (Ejemplo: Si número= 23996 entonces la suma de los dígitos es 29). Análisis: La suma de los dígitos de un número entero se obtiene sumando la cifra de las unidades y las cifras del número resultante de la división entera del número inicial por 10. La cifra de las unidades se obtiene mediante la operación resto entero entre 10. Si el número es menor que 10 obtenemos la condición de salida.

Page 38: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 38

PROGRAM SumaDigitos; Var n: Integer; FUNCTION Suma (n: Integer): Integer; Begin If n < 10 then Suma := n else Suma := n mod 10 + Suma (n div 10) End; Begin ClrScr; Write (‘Introduzca un número entero: ’); Readln (n); Writeln (‘La suma de los dígitos de: ‘, n, ‘es ‘ , Suma (n) ) End. Ejercicio 4 Programar en Pascal un algoritmo que solicite 2 números enteros, los sume, e imprima el resultado en pantalla en la posición (38, 12). Utilizar procedimientos. program suma (input, output); uses crt; type MiEntero = integer; var num1, num2, resultado: MiEntero; procedure carga_numeros; begin clrscr; write('Ingrese un numero entero: '); readln(num1); write('Ingrese otro numero entero: '); readln(num2); end;

No es bueno, porque usa variables globales.

Page 39: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 39

procedure suma_numeros; begin resultado:=(num1 + num2); end; procedure muestra_resultado; begin clrscr; gotoxy(38,12); writeln('Resultado = ', resultado); end; begin {Prog. Ppal.} carga_numeros; suma_numeros; muestra_resultado; end. Versión mejorada (sin usar variables globales) program suma (input, output); {SIN USAR VARIABLES GLOBALES} uses crt; type MiEntero = integer; var num1, num2, resultado: MiEntero; procedure carga_numeros(var x,y: integer); begin clrscr; write('Ingrese un numero entero: '); readln(x); write('Ingrese otro numero entero: '); readln(y); end;

Page 40: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 40

procedure suma_numeros(a,b: integer; var result: integer); begin result:=(a + b); end; procedure muestra_resultado(res: integer); begin clrscr; gotoxy(38,12); writeln('Resultado = ', res); end; begin {Prog. Ppal.} carga_numeros(num1,num2); suma_numeros(num1,num2,resultado); muestra_resultado(resultado); end. Ejercicio 5 Deducir que hace el siguiente algoritmo. program ambito_de_variables (input, output); uses crt; type MiEntero = word; var a: MiEntero; procedure imprime; var a: MiEntero; procedure imprime; var a: MiEntero;

Page 41: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 41

procedure imprime; var a: MiEntero; begin a:=0; writeln(a); end; begin a:=1; imprime; writeln(a); end; begin a:=2; imprime; writeln(a); end; begin {Prog. Ppal.} clrscr; a:=3; imprime; writeln(a); end. RTA: Imprime los números 0, 1, 2 y 3. Ejercicio 6 Desarrollar un programa Pascal que calcule la potencia de un número (tanto la potencia como la base son ingresados por el usuario), utilizando funciones. Informar el resultado por pantalla. program CalcularPotencia(input,output); uses crt;

Page 42: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 42

type MiEntero = longint; var base, potencia: MiEntero; function calcula_potencia(num1, num2: MiEntero): MiEntero; var resultado, i: MiEntero; begin resultado:=1; for i:=1 to num2 do resultado:=(num1 * resultado); calcula_potencia:=resultado; end; begin {Prog. Ppal.} clrscr; write('Ingrese un numero: '); readln(base); write('Ingrese la potencia: '); readln(potencia); writeln(base, ' elevado a la ', potencia, ' es igual a ', calcula_potencia(base,potencia)); end. Versión mejorada (acepta exponentes negativos) program CalcularPotencia(input,output); {ACEPTA EXPONENTES NEGATIVOS} uses crt; type MiEntero = longint; MiReal = real; var base, potencia: MiEntero; function calcula_potencia(num1, num2: MiEntero): MiReal;

Falla cuando el exponente es negativo

Page 43: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 43

var resultado: MiReal; i: MiEntero; begin resultado:=1; for i:=1 to abs(num2) do resultado:=(num1 * resultado); if (num2<0) then resultado:=(1/resultado); calcula_potencia:=resultado; end; begin {Prog. Ppal.} clrscr; write('Ingrese un numero: '); readln(base); write('Ingrese la potencia: '); readln(potencia); writeln(base, ' elevado a la ', potencia, ' es igual a ', calcula_potencia(base,potencia):6:5); end. Ejercicio 7 Construir un programa en Pascal que lea un número de 3 cifras positivo por teclado, luego emita un número de 3 cifras positivo al azar, y finalmente compare ambos números, informando por pantalla si es menor, mayor ó igual. program azar (input, output); uses crt; type MiEntero = integer; MiChar = char; var numero: MiEntero; opcion: MiChar; procedure carga_numero(var numero: MiEntero);

Page 44: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 44

begin write('Ingrese un numero entero: '); readln(numero); end; function numero_aleatorio: MiEntero; begin randomize; numero_aleatorio:=random(1000); {Retorna un entero entre 0 y 999} end; procedure informa(num1, num2: MiEntero); begin if (num1<num2) then writeln(num1, ' es menor que ', num2) else if (num1>num2) then writeln(num1, ' es mayor que ', num2) else begin writeln(num1, ' es igual que ', num2); writeln('Usted ha acertado!!!'); end; end; begin {Prog. Ppal.} repeat clrscr; carga_numero(numero); informa(numero,numero_aleatorio); writeln; write('C=Continua S=Sale ?'); opcion:=readkey; until upcase(opcion)='S'; end.

Page 45: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO III

Vectores y Matrices

Page 46: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 46

VECTORES

Los vectores, también conocidos como arreglos o arrays, son una de las estructuras más útiles en programación. Dicho en forma sencilla, un vector es una variable estructurada que permite almacenar varios datos, y presenta las siguientes características: Es una estructura homogénea: Esto quiere decir que todos los datos almacenados en dicha estructura son del mismo tipo (por ejemplo: todos del tipo integer o bien todos del tipo char). Es una estructura lineal de acceso directo: Esto significa que es unidimensional (alcanza un solo subíndice para indicar un determinado elemento de la estructura) y que los datos pueden ser accedidos en forma directa, es decir, con sólo proporcionar su posición (en forma de subíndice). Es una estructura estática: Su tamaño (número y tipo de elementos) se define en tiempo de diseño y no cambia durante la ejecución del programa. Los vectores están caracterizados por un nombre, un tipo y por su cantidad de elementos. Gráficamente, puede visualizárselos de la siguiente manera:

Notas

Supongamos para este ejemplo que en un curso de 10 alumnos se desea almacenar, para cada estudiante, las calificaciones de su primer parcial. Para ello, definiremos un vector, al que llamaremos Notas, que contendrá 10 elementos del tipo entero (integer). Notemos aquí que, tal como se indicó anteriormente, tenemos una estructura en la que todos los datos son del mismo tipo, en la que podemos conocer un dato con sólo especificar su posición, y que es claramente lineal. Cabe aclarar que no necesariamente el arreglo debe empezar con el subíndice 1, podría hacerlo por el 0, o por cualquier otro valor. En este caso en particular, resultaría práctico hacer coincidir la nota de cada alumno con su lugar en la lista (que empieza por el 1).

1 6

2 4

3 2

4 7

5 5

6 6

7 1

8 8

9 4

10 9

Page 47: Apunte Teorico(AlgoritmosI)(VERSION3)

Declaración en lenguaje Pascal Para poder hacer uso de una variable estructurada en forma de vector, debemos en primer lugar crear un tipo (en la sección de tipos del programa) de la siguiente manera: Type [nombre del tipo] = array[primer subíndice .. último subíndice] of [tipo de los datos que se almacenarán] En el caso del ejemplo de las notas, escribiríamos: Type tVector = array [1..10] of integer; Luego de definido el tipo, podemos crear una variable de dicho tipo, según la siguiente sintaxis: Var [nombre de la variable] : [nombre del tipo definido]; Para el ejemplo: Var Notas : tVector; También es posible (aunque menos recomendable por hacer más difícil de entender el programa) escribir solamente: Var Notas : array [1..10] of integer; De esta forma obtendremos una variable estructurada de nombre Notas, que en cada posición contendrá una variable atómica. Para invocar un dato del vector, por ejemplo el de la posición 3, debemos utilizar la forma Notas [3], lo que arrojaría el valor entero 2 (según nuestro ejemplo, el tercer alumno obtuvo una calificación de 2). Es decir, se debe especificar entre corchetes el subíndice de Notas al que se desea acceder. Es importante recordar que mientras Notas es una variable estructurada, Notas[3] es una variable atómica, ya que su tipo es integer. Completemos el ejemplo escribiendo un pequeño algoritmo que permita al usuario ingresar secuencialmente las notas de los 10 alumnos, y luego las muestre por pantalla: Program notas_alum; Uses crt; Const ALUM=10; {Usamos una constante por si cambia el número de

alumnos} Type tVector = array [1..ALUM] of integer; Var Notas: tVector; Procedure toma_datos (var vector: tVector);

Page 48: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 48

Var i: integer; Begin For i:=1 to ALUM do Begin Writeln (‘Ingrese la nota del alumno ’, i,’ de la

lista’); Readln (vector[i]); End; Writeln (‘Carga de datos finalizada’); End; Procedure muestra_datos (var vector: tVector); Var i :integer; Begin Writeln('Notas de los alumnos:'); For i:=1 to ALUM do Writeln (i: 3,’: ‘,vector[i]); End; Begin Clrscr; toma_datos(Notas); writeln; muestra_datos(Notas); readkey; End. Puede notarse en el ejemplo que se pasa el vector como parámetro, y que dicho pasaje se hace por referencia. Esto es así por dos razones: en primer lugar se desea que el procedimiento toma_datos modifique el contenido del vector Notas, y no de una copia local como sucedería si se pasara el vector por valor; y en segundo lugar, aunque pudiera trabajarse con una copia (por ejemplo, porque sólo se quisieran mostrar los contenidos del vector y no modificar su contenido) como podría hacerse en el caso del procedimiento muestra_datos, debe tenerse en consideración que las estructuras grandes consumen recursos (ocupan memoria) haciendo poco recomendable su duplicado. Por esta razón al pasar por parámetros una variable estructurada grande, se acostumbra hacerlo por referencia (si bien en este ejemplo en particular no trabajamos con una estructura tan grande, no resulta inapropiado tener esto en cuenta, especialmente en el caso de las matrices, que son arreglos multidimensionales que se verán a continuación). Finalmente, vale aclarar que los subíndices no tienen siquiera que ser números, basta con que sean de un tipo ordinal. De hecho, podríamos hacer una declaración de la siguiente forma: type tVector = array [‘A’..’D’] of integer y una variable miVector del tipo tVector tendría 4 elementos enteros: miVector[‘A’], miVector[‘B’], miVector[‘C’] y miVector[‘D’]. Notar incluso que pueden utilizarse constantes, como en el ejemplo dado (en el que se usa ALUM). No puede, sin embargo, utilizarse variables, ya que los arreglos son estructuras estáticas (su número de elementos no puede variar mientras se ejecuta el programa).

Page 49: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 49

Veamos un pequeño ejercicio final Ejercicio: Construir un algoritmo que efectúe la suma y el promedio de una serie de 20 números enteros utilizando arrays y muestre los resultados por pantalla. Los números son ingresados por el usuario. Solución propuesta: Program vectores; Type tArray = array [1..20] of integer; Var vector: tArray; Suma: integer; i: integer; Begin Suma:=0; For i:=1 to 20 do Begin Writeln(‘Elemento’,i,’:’); Readln (vector[i]); Suma:= Suma + vector[i]; End; Writeln (‘suma = ’,suma); Writeln (‘promedio = ‘, suma/20: 10: 2); End. Strings El lenguaje Pascal incluye un tipo predefinido string que permite almacenar cadenas de caracteres. Al declarar una variable de este tipo, se hace de la forma miVariable: String [max] donde max es la cantidad máxima de caracteres que tendrá la cadena. Puede omitirse la sección de los corchetes y se asignará por defecto el valor 255. A esta altura, estamos en condiciones de visualizar que dicho tipo de datos es básicamente un array de caracteres. De hecho, si se dispone de una variable string de nombre miCadena que se declaró mediante var miCadena: string [4]; y se asigna durante el programa miCadena:=’hola’; , la sentencia writeln (miCadena [2]); escribirá la letra o en pantalla, es decir el segundo elemento de la cadena. En Pascal, el tipo string admite hasta una longitud de 255 caracteres. En rigor de verdad, Pascal trata ligeramente distinto al tipo string que le es propio, por un lado, y a la popular forma de definir cadenas como arrays de char terminados por un carácter especial (null), por otro. Esta última definición es la que usan muchas de las funciones incluidas en la librería estándar Strings. De todas formas, es sencillo convertir entre ambas definiciones.

Page 50: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 50

Operaciones en y entre vectores En el Capítulo IV: Métodos de Búsqueda, pueden consultarse los procedimientos de Mezcla de Listas, los métodos de ordenamiento (burbuja, selección e inserción), y los métodos de búsqueda (secuencial y binaria) en vectores.

Page 51: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 51

MATRICES

Hasta ahora hemos visto arreglos unidimensionales. Es decir, que podíamos imaginar la estructura como “una línea de variables una encima de la otra”. No obstante, podríamos también imaginar estructuras rectangulares, o incluso cúbicas. Veamos el caso de un tablero e Ta-Te-Ti: Vemos aquí una estructura de dos dimensiones, de 3x3, donde cada posición puede ser determinada unívocamente proporcionando 2 subíndices (uno para las filas y otro para las columnas). A esta estructura se la denomina matriz (de 2 dimensiones). Las matrices, al ser una extensión de los vectores, responden a las propiedades ya enunciadas para los arreglos. Recordemos brevemente: todos los datos que se almacenarán en la matriz son del mismo tipo (homogeneidad), la cantidad de elementos no cambia durante la ejecución del programa (ni el tipo) por lo que se dice que es estática, y es una estructura de acceso directo (basta con dar una posición, en este caso en la forma de dos subíndices, para acceder a un dato específico) aunque en vez de ser lineal es rectangular, cúbica o de las dimensiones correspondientes. Las matrices, análogamente respecto de los arreglos, tienen un nombre, un tipo de elementos que almacenan y un tamaño. Declaración en lenguaje Pascal Al igual que hicimos con los arreglos, para hacer uso de una variable estructurada en forma de matriz, crearemos primeramente un tipo (en la sección de tipos del programa) de la siguiente manera: Type [nombre del tipo] = array [1ro..último, 1ro..último] of [tipo de los datos que se almacenarán] Notemos la diferencia con respecto a los arreglos: se ha agregado un segundo rango de subíndices. En el caso del ejemplo del tablero, escribiríamos: Type tMatriz = array [1..3, 1..3] of char;

1 2 3

1 X X

2 X O

3 O

Page 52: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 52

Desde ya que el hecho de que la estructura sea cuadrada se debe simplemente a que esa es una característica del tablero del juego de Ta-Te-Ti. La matriz puede por supuesto no ser cuadrada, sino simplemente rectangular. Luego de definido el tipo, podemos crear una variable de dicho tipo, según la sintaxis ya vista: Var [nombre de la variable] : [nombre del tipo definido]; Para el ejemplo: Var Tablero : tMatriz; También es posible (aunque menos recomendable por hacer más difícil de entender el programa) escribir solamente: Var Tablero: array [1..3, 1..3] of char; Así obtendremos una variable estructurada de nombre Tablero, que en cada posición contendrá una variable atómica, en nuestro caso del tipo carácter, que será X, O, o bien un espacio. Para invocar un dato de la matriz, por ejemplo el que se encuentra en el centro, debemos utilizar la forma Tablero [2,2], y esto correspondería según nuestro juego a la letra X. Es decir, se deben especificar entre corchetes los subíndices para la fila y la columna, separados por una coma. Es importante recordar que mientras el Tablero es una variable estructurada, Tablero [2,2] contiene un dato atómico, ya que su tipo es char. Veamos un procedimiento sencillo para cargar todas las posiciones de la matriz Tablero con espacios (estado inicial del partido de Ta-Te-Ti): Procedure cargaMatriz (var matriz: tMatriz); Var i, j: integer; Begin For i:=1 to 3 do For j:=1 to 3 do matriz [i, j]:=’ ‘; End; Y finalmente un procedimiento que muestre por pantalla el tablero en cualquier momento del partido: Procedure muestraMatriz (var matriz: tMatriz); Var i, j: integer; Begin For i:=1 to 3 do Begin For j:=1 to 3 do Write (matriz [i, j]:2); Writeln; {Pasar a la siguiente fila}

Page 53: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 53

End; End; En este último caso no es estrictamente necesario pasar por referencia la matriz, aunque para una estructura grande economiza memoria hacerlo de esa manera.

Page 54: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 54

MATRICES (EJEMPLOS)

Ejemplo 1 Construir un programa Pascal que permita cargar una matríz (de enteros) de 2 dimensiones M y N, donde M (filas) y N (columnas) son ingresados por el usuario. Luego, que imprima la matríz en pantalla. program matrices; uses crt; const MAX_RANGO = 50; type RANGO = 1..MAX_RANGO; MiEntero = integer; tMatriz = Array[RANGO,RANGO] of MiEntero; var Matriz: tMatriz; m, n: RANGO; procedure carga(var mat: tMatriz; m,n: RANGO); var i,j: RANGO; begin clrscr; for i:=1 to m do {Recorre las filas} for j:=1 to n do {Recorre las columnas} begin write('Ingrese el valor [', i, ', ', j, '] = '); readln(mat[i,j]); end; readkey; end; procedure imprime(mat: tMatriz; m,n: RANGO);

Page 55: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 55

var i,j: RANGO; begin clrscr; for i:=1 to m do begin for j:=1 to n do write(mat[i,j]:5); writeln; end; readkey; end; begin {Prog. Ppal.} clrscr; write('Ingrese la cantidad de filas: '); readln(m); write('Ingrese la cantidad de columnas: '); readln(n); carga(matriz,m,n); imprime(matriz,m,n); end. NOTA: como tarea para el hogar, adaptar el programa anterior para cargar e imprimir matrices de 3 dimensiones. Ejemplo 2 Construir un programa Pascal que defina una matríz para ser utilizada como tablero de ajedréz, y que además inicialize todas las posiciones del mismo. program ajedrez; uses crt; const MAX_RANGO = 8; type RANGO = 1..MAX_RANGO; tTablero = array[RANGO,RANGO] of string[3];

Page 56: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 56

var tablero: tTablero; procedure inicializa(var tablero: tTablero); var i,j: RANGO; begin for i:=1 to MAX_RANGO do for j:=1 to MAX_RANGO do tablero[i,j]:='X'; {X = casilla desocupada} tablero[1,1]:='TB'; {TB = torre blanca} tablero[1,2]:='CB'; {CB = caballo blanco} tablero[1,3]:='AB'; {AB = alfil blanco} tablero[1,4]:='RYB'; {RYB = rey blanco} tablero[1,5]:='RAB'; {RAB = reina blanca} tablero[1,6]:='AB'; tablero[1,7]:='CB'; tablero[1,8]:='TB'; tablero[8,1]:='TN'; {TN = torre negra} tablero[8,2]:='CN'; {CN = caballo negro} tablero[8,3]:='AN'; {AN = alfil negro} tablero[8,4]:='RYN'; {RYN = rey negro} tablero[8,5]:='RAN'; {RAN = reina negra} tablero[8,6]:='AN'; tablero[8,7]:='CN'; tablero[8,8]:='TN'; for j:=1 to MAX_RANGO do begin tablero[2,j]:='PB'; {PB = peon blanco} tablero[7,j]:='PN'; {PN = peon negro} end; readkey; end; procedure imprime(tablero: tTablero); var i,j: RANGO; begin clrscr; for i:=1 to MAX_RANGO do

Page 57: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 57

begin for j:=1 to MAX_RANGO do write(tablero[i,j]:5); writeln; end; readkey; end; begin {Prog. Ppal.} inicializa(tablero); imprime(tablero); end. Ejemplo 3 Construir un programa Pascal en base al ejemplo anterior que calcule, si puede, cuántas semillas debería haberle dado el Rey Shirham a su ministro por la invención del juego de ajedréz, e imprima por pantalla el tablero con la cantidad de semillas por casillero. Utilizar para los cálculos el tipo LONGINT, y ver qué sucede. Luego utilizar el tipo DOUBLE, y volver a ejecutar el programa. Analizar si los resultados devueltos son correctos. program ajedrez; uses crt; const MAX_RANGO = 8; type RANGO = 1..MAX_RANGO; MiNumero = longint; {Probar lo que sucede con el tipo DOUBLE} tTablero = array[RANGO,RANGO] of MiNumero; var tablero: tTablero; procedure llena_tablero(var tablero: tTablero); var i,j: RANGO; begin tablero[1,1]:=1; for i:=1 to MAX_RANGO do begin

Page 58: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 58

for j:=2 to MAX_RANGO do tablero[i,j]:= ( 2 * tablero[i,j-1] ); if (i<MAX_RANGO) then tablero[i+1,1]:= ( 2 * tablero[i,MAX_RANGO]); end; end; procedure imprime(tablero: tTablero); var i,j: RANGO; begin clrscr; for i:=1 to MAX_RANGO do begin for j:=1 to MAX_RANGO do write(tablero[i,j]:10); writeln; end; end; function cant_semillas(tablero: tTablero): MiNumero; var i,j: RANGO; total: MiNumero; begin total:=0; for i:=1 to MAX_RANGO do for j:=1 to MAX_RANGO do total:=total + tablero[i,j]; cant_semillas:=total; end; begin {Prog. Ppal.} llena_tablero(tablero); imprime(tablero); writeln; writeln('**> Cant. de semillas que el Rey debe dar = ',

cant_semillas(tablero)); readkey; end.

Page 59: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO IV

Métodos de Búsqueda

Page 60: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 60

MÉTODOS DE BÚSQUEDA

La búsqueda es una operación que tiene por objeto la localización de un elemento dentro de la estructura de datos. El elemento que se busca se caracteriza o distingue por el valor de una clave o la combinación de varias si la clave es compleja. En lo sucesivo entenderemos por clave el valor, ya sea simple o no, que caracteriza al elemento. Siendo el array de una dimensión o lista una estructura de acceso directo (búsqueda binaria) y a su vez de acceso secuencial (búsqueda secuencial). Búsqueda secuencial Consiste en recorrer el array elemento a elemento e ir comparando con la clave o valor buscado. El recorrido termina al encontrar el elemento o bien porque se rebasa la posición del último componente del array. La búsqueda puede realizarse en arrays desordenados u ordenados de acuerdo a la clave buscada. PROGRAM BUSQUEDAS; CONST N = 100; TYPE TipoDato = integer; TipoIndice = 1 .. N; TipoPosicion = 0 .. N; TipoArray= array [TipoIndice] of TipoDato; VAR Datos : TipoArray; Clave : TipoDato; Posicion: TipoPosicion; PROCEDURE BUSCAR (Var V: TipoArray; Valor: TipoDato; Var P: TipoPosicion); BEGIN P := 0; Repeat P := P + 1 Until (V[P] = Valor) or (P=N); If V[P] <> Valor then P := 0 END; * declaración de otros procedimientos y/o funciones*

Page 61: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 61

BEGIN Cargar (Datos); *proc. de generación del array* Readln (Clave); Buscar (Datos, Clave, Posicion); If Posición = 0 then Writeln (“No se encuentra el valor en el array”) else Writeln (“Encontrado en posición”, Posicion) END. Si la búsqueda se realiza en un array ordenado ésta puede realizarse secuencialmente o bien en forma binaria. Si la búsqueda se realiza en forma secuencial finalizará al encontrar el elemento o al detectar un componente del array con clave mayor que el valor buscado. Ejemplo: PROCEDURE BUSCAR (Var V: TipoArray; Valor: TipoDato; Var P: TipoPosicion); BEGIN P := 0; Repeat P := P + 1 Until (V[P] >= Valor) or (P=N); If V[P] <> Valor then P := 0 END; Búsqueda binaria Es el método más eficiente para encontrar elementos en un array ordenado. El proceso comienza comparando el elemento central con el valor buscado. Si ambos coinciden finaliza la búsqueda. Si no ocurre así, el valor buscado será mayor o menor estricto que el central, la búsqueda binaria continúa en el subarray inferior o superior y finalizará cuando encuentre el elemento o cuando el subarray se quede sin elementos. Este método exige que la lista esté ordenada. PROGRAM BUSCAR; CONST Inferior = 1; Superior = 10; TYPE

Page 62: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 62

Indice = Inferior .. Superior; Lista = Array[Indice] of Integer; VAR A: Lista; Clave: Integer; Posición: Indice; PROCEDURE BUSQUEDA (Var A: Lista; Inferior,Superior,Clave: Integer; Var Posicion: Integer); VAR Central, Bajo, Alto: Integer; BEGIN Bajo := Inferior; Alto := Superior; Posicion := 0; Repeat Central := (Bajo + Alto) div 2; If Clave = A [Central] then Posicion := Central else If Clave < A [Central] then Alto := Central-1 else Bajo := Central+1 until (Posicion <> 0) or (Bajo > Alto); If Bajo > Alto then Posicion := Central else Posicion := 0 END; BEGIN * pgm. ppal.* For I := Inferior to Superior do Readln A[I]; Write (“¿Qué número desea buscar : ?”); Readln (Clave); Busqueda (A, Inferior, Superior, Clave, Posicion); If Posicion <> 0 then Writeln (Clave, “está en la posición”, Posicion); else Writeln (Clave, “no está en el array”); END. A continuación se muestra un subprograma (función) que realiza búsqueda binaria recursiva.

Page 63: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 63

FUNCTION BINARIA (A: Lista; Inferior, Superior, Clave: Integer): Integer; VAR Central: Integer; BEGIN If Inferior > Superior then Binaria := 0 else BEGIN Central := (Inferior+Superior) div 2; If Clave = A [Central] then Binaria := Central else If Clave > A [Central] then Binaria := Binaria (A, Central+1, Superior, Clave) else Binaria := Binaria (A, Inferior, Central-1, Clave) END END;

MÉTODOS DE ORDENACIÓN

Se supone definido en el programa un tipo general: CONST N = 100; TYPE TipoArray = Array [1 .. N] of Integer; Ordenación por intercambio (u ordenación por burbuja) Este método es el menos eficiente e implica comparar los elementos adyacentes e intercambiarlos si estos no guardan el orden deseado. En pasadas sucesivas se irán colocando en las posiciones más altas los inmediatamente menores. Por tanto, el número de pasadas necesarias para que el array resulte ordenado es tantas como elementos menos uno (N-1), ya que en la última pasada se colocarán los dos elementos más pequeños en sus posiciones finales. PROCEDURE BURBUJA (Var A: TipoArray; N: Integer); VAR

Page 64: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 64

Recorrido, J: Integer; BEGIN For Recorrido := 1 to N-1 do For J := 1 to N-Recorrido do If A [J] > A [J+1] then INTERCAMBIAR ( A [J], A [J+1] ) END; PROCEDURE INTERCAMBIAR (Var a,b: Integer); VAR Aux: Integer; BEGIN Aux := a; a := b; b := Aux END; Ordenación por selección Se basa en dos principios básicos: 1.- Seleccionar el elemento más pequeño (o más grande) del array. 2.- Colocarlo en la posición más baja (o más alta) del array. El número de recorridas del array es N-1, pues en la última pasada se colocan los dos elementos más grandes en la parte superior del array. PROCEDURE SELECCION (Var A: TipoArray; N: Integer); VAR Recorrido, J: Integer BEGIN For Recorrido := 1 to N-1 do For J := Recorrido+1 to N do If A [Recorrido] > A [J] then INTERCAMBIAR ( A [Recorrido], A [J] ) END; A diferencia del método de la burbuja, en cada pasada o recorrida es el elemento más pequeño el que se coloca en la posición final que le corresponde. Ordenación por inserción

Page 65: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 65

Dado que un array con un único elemento (A [1] ) es un array ordenado, si el array contiene N elementos, el número de inserciones necesarias para ordenar completamente el array será N-1, y empezará a realizarse la primera a partir de A[2] hasta A[N]. PROCEDURE INSERCIÓN (Var A: TipoArray; N: Integer); VAR Indice, K, Aux: Integer; Hallado: Boolean; BEGIN For Indice := 2 to N do Begin Aux := A [Indice]; K := Indice-1; *Buscar posición en subarray Hallado := false; ordenado de 1..Indice-1* While not Hallado and (K > 0) do If A [K] > Aux then Begin A [K+1] := A [K]; *Desplazar elementos K := K-1 mayores a indices

superiores* End else Hallado := true; If Hallado then A [K+1] := Aux *Posición de inserción K+1* else A[1] := Aux *Posición de inserción 1* End END; El método de búsqueda realizado es secuencial, pero puede optimizarse empleando la búsqueda binaria, en cuyo caso el método recibe el nombre de ordenación por inserción binaria.

MEZCLA DE LISTAS

Consiste en formar arrays ordenados a partir de otros previamente ordenados. El procedimiento de mezcla no consiste en disponer los dos subarrays en otro mayor y ordenar el conjunto, sino en aprovechar el hecho de que ya existe un orden parcial. Se comparan sendos elementos de cada uno de los arrays, y se introduce el más pequeño en el array destino, tomando el siguiente elemento del array correspondiente, hasta agotar todos los elementos de uno de ellos. Finalmente se copiarán los elementos no procesados del otro array.

Page 66: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 66

El procedimiento que sigue mezcla dos subarrays A y B de M y N elementos respectivamente, en un array C que tendrá M + N elementos. PROCEDURE MEZCLA (A, B: Lista; Var C: Lista; M, N: Integer); VAR I1, Y2, K : Integer; BEGIN I1 := 1; *Se toma el primer elemento de cada uno de I2 := 1; los arrays ordenados* K := 1; While ( I1 <= M ) and ( I2 <= N ) do Begin If A [I1] <= B [I2 ] then Begin C [ K ] := A [I1]; I1 := I1 + 1 End else Begin C [ K ] := B [I2]; I2 := I2 + 1 End; K := K + 1 End; If I1 > M then For P := I2 to N do Begin C [ K ] := B [P]; K := K + 1 End else For P := I1 to M do Begin C [ K ] := A [P]; K := K + 1 End END;

Page 67: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO V

Registros

Page 68: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 68

REGISTROS

El registro, y su tratamiento en archivos, constituyen las unidades fundamentales de tratamiento en Informática de gestión. Bajo el concepto de registro de define una estructura de datos heterogéneos, denominados campos, y a los que se diferencia de un array pues se accede por nombre y no por medio de un índice. Los campos guardan una relación de dependencia entre ellos. El dato de tipo registro (Record) debe ser declarado en la sección de tipos, antes de poder ser utilizado. Ejemplo: TYPE Tipo1 = ...; Tipo2 = ...; TipoRegistro = Record Campo1 : Tipo1; Campo2 : Tipo2 End; VAR Registro : TipoRegistro; El acceso a la variable registro se realiza mediante el nombre de la variable registro y el nombre del campo separados por un punto. Para abreviar la escritura se utiliza la sentencia With. Ejemplo: With Alumno do Begin Write (‘Introduzca Número de Padrón:’); Readln (Padron); *Readln (Alumno.Padron)* Materia := 7502 *Alumno.Materia := 7502* End; La única operación que se puede hacer con una variable tipo registro como tal es la asignación, es decir, se pueden copiar todos los campos de una variable registro a otra variable registro del mismo tipo, utilizando la sentencia de asignación. Si Persona y Cliente son variables de mismo tipo registro, definidas en la declaración: VAR Persona, Cliente: Empleado;

Page 69: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 69

Se puede escribir la sentencia: Persona := Cliente; Un registro puede ser pasado como parámetro a una función o procedimiento como parámetro actual, siempre y cuando el correspondiente parámetro formal sea del mismo tipo. El procedimiento LeerEmpleado se puede utilizar para leer los campos de una variable registro de tipo Empleado. La variable registro se pasa por variable y sólo se necesita un parámetro en lugar de tres. PROCEDURE LeerEmpleado (Var Trabajador: Empleado); VAR LetraSexo: Char; *Letra que indica el sexo* BEGIN With Trabajador do Begin Write (‘DNI:’); Readln (DNI); Write (‘Nombre:’); Readln (Nombre); Write (‘Sexo: V o M’); Readln (LetraSexo); Case LetraSexo of ‘V’, ‘v’ : Sexo := Varon; ‘M’, ‘m’ : Sexo := Mujer end; End END; Registros jerárquicos Los campos de los registros pueden ser de cualquier tipo definido por el usuario, incluso también registros. Un registro con uno o más campos de tipo registro se denomina registro jerárquico. Por ejemplo, un registro que contenga el nombre y fecha de cumpleaños de una persona puede ser declarado de la siguiente forma: TYPE Fecha = Record Dia : 1..31; Mes : 1..12; Anno : Integer End; Info = Record

Page 70: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 70

Nombre : String [30]; CumpleAnnos : Fecha End; VAR Persona : Info; donde con Persona.CempleAnnos.Dia accederíamos al campo Dia. Es posible anidar sentencias With con registros jerárquicos. With Persona do With CumpleAnnos do Writeln (‘Día:’, Dia, ‘Mes:’, Mes, ‘Año:’, Anno); Registros variantes En Pascal es posible definir tipos registro, que tengan algunos campos que son iguales para todas las variables de ese tipo (parte fija) y algunos campos que pueden ser diferentes o variables (parte variante). Por ejemplo se pueden incluir información adicional sobre un empleado basado en su estado civil. Para todos los empleados casados se puede desear conocer el nombre del cónyuge y el número de hijos; para todos los empleados divorciados, se puede desear conocer la fecha del divorcio y para todos los empleados solteros se puede desear conocer si viven solos o no. Ejemplo: TYPE Empleado = Record ... End; Estado = (Casado, Divorciado, Soltero, Viudo); Ejecutivo = Record Datos : Empleado; case Estado of Casado: (NombreConyuge: String [30]; NumHijos: Integer); Divorciado: (FechaDivorcio: Fecha); *Fecha es otro tipo definido* Soltero: (Solo: Boolean); Viudo: () (*carece de campos*) End; (*del Record*)

Page 71: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 71

Ejemplo: TYPE Forma = (Triangulo, Cuadrado, Circulo); Tinta = (Rojo, Amarillo, Verde, Azul); Figura = Record Color : Tinta; Case Forma of Triangulo : (Base, Altura: Real); Cuadrado : (Lado: Real); Circulo : (Radio: Real); End; (*record*) La parte variante se especifica con las palabras reservadas CASE .. OF y debe declararse inmediatamente después de la parte fija. Sólo se admite una parte variante y los campos de la parte variante pueden ser a su vez variantes. Si alguna de las opciones o valores de la parte variante carece de definición de campos se indicaría con un par de paréntesis vacíos ‘()’. No debe confundirse el CASE ..OF de la definición de la parte variante con una estructura o sentencia CASE..OF pues debe observarse la falta del END que la cierra. Constantes tipo registro Turbo Pascal permite, al contrario que Pascal estándar, definir constantes tipo registro. Estas constantes son en realidad variables que se inicializan a los valores indicados en la declaración correspondiente, pero que pueden ser modificadas durante la ejecución del programa. La declaración de este tipo de constantes necesita de la declaración previa de su tipo y su posterior inicialización para cada uno de sus campos, tal como se indica en el siguiente ejemplo: TYPE Empleado = Record Nombre : String [30]; Edad : Integer; End; CONST NumReg : Empleado = (Nombre: ‘Mortimer’; Edad : 44);

Page 72: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 72

REGISTROS (EJEMPLOS) Ejemplo 1 En una empresa de turismo se desea saber cuál es el país más visitado por sus clientes y cuál es el medio de transporte más elejido para visitar dicho país. Para ello se cuenta con un vector con el listado de países, y cuántas veces se ha viajado a cada uno de ellos en diferentes medios de transporte. Se pide también un procedimiento que permita cargar los datos. El vector no está ordenado por ningún campo en particular. program turismo; uses crt; const MAXCANTDESTINOS = 100; type tDestino = record

pais: string[30]; medio: string[30]; cantVeces: integer; end; tVector = array[1..MAXCANTDESTINOS] of tDestino; var lista: tVector; cantDestinos: integer; destino: tDestino; procedure CargaDatos(var lista: tVector; var n: integer); var destino: tDestino; opcion: char; begin n:=0; repeat begin write('Pais de destino: '); readln(destino.pais); write('Medio de transporte: '); readln(destino.medio);

Page 73: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 73

write('Cant. de viajes: '); readln(destino.cantVeces); write('Otro destino? (s/n): '); readln(opcion); inc(n); lista[n]:=destino; end; until upcase(opcion)='N'; end; procedure BuscaMasVisitado(lista: tVector; n: integer; var destino: tDestino); var i: integer; begin destino:=lista[1]; for i:=2 to n do if (lista[i].cantVeces>destino.cantVeces) then destino:=lista[i]; end; begin {Prog. Ppal.} clrscr; writeln('EMPRESA DE TURISMO'); writeln; CargaDatos(lista,cantDestinos); writeln; BuscaMasVisitado(lista,cantDestinos,destino); writeln('El pais mas visitado es: ', destino.pais); writeln('Medio de transporte: ', destino.medio); writeln('Cantidad de visitas: ', destino.cantVeces); writeln; write('Presione cualquier tecla para salir...'); readkey; end. Ejemplo 2 Se cargan en un vector los datos de los alumnos de Algoritmos I, incluyendo:

- Padrón, - Nombre y Apellido, - Notas (del 1er., 2do. y 3er. parcial).

Page 74: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 74

Se desea construir:

- Un procedimiento que calcule la nota final de cada alumno y la almacena en la lista.

- Un procedimiento que ordene la lista completa por número de padrón de menor a mayor.

- Un procedimiento para dar de alta a un nuevo alumno. - Un procedimiento para visualizar por pantalla la lista de los alumnos con sus

respectivos padrones y notas finales. program alumnos; uses crt; const MAXCANTALUMNOS = 100; type tNotas = record parcial1, parcial2, final: real; end; tAlumno = record padron: longint; nomyap: string[50]; notas: tNotas; end; tVector = array[1..MAXCANTALUMNOS] of tAlumno; var lista: tVector; cantAlumnos: integer; opcion: char; function binaria(lista: tVector; padron, n: integer): integer; var ls,li,medio,posicion: integer; begin li:=1; ls:=n; posicion:=0; repeat begin

Page 75: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 75

medio:=(li+ls) div 2; if (padron=lista[medio].padron) then posicion:=medio else if (padron<lista[medio].padron) then ls:=medio-1 else li:=medio+1; end; until (posicion<>0) or (li>ls); binaria:=posicion; end; procedure altas(var lista: tVector; var n: integer); var alumno: tAlumno; opcion: char; begin repeat begin write('Padron: '); readln(alumno.padron); write('Nombre y apellido: '); readln(alumno.nomyap); write('Nota 1er. parcial: '); readln(alumno.notas.parcial1); write('Nota 2do. parcial: '); readln(alumno.notas.parcial2); inc(n); lista[n]:=alumno; write('Otro alumno? (s/n): '); readln(opcion); end; until upcase(opcion)='N'; end; procedure ordena(var lista: tVector; n: integer); var recorrido,i: integer; aux: tAlumno; begin for recorrido:=1 to n-1 do

Page 76: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 76

for i:=recorrido+1 to n do if (lista[recorrido].padron>lista[i].padron) then begin aux:=lista[recorrido]; lista[recorrido]:=lista[i]; lista[i]:=aux end; end; procedure listado(lista: tVector; n: integer); var i: integer; begin for i:=1 to n do begin writeln('Padron: ', lista[i].padron); writeln('Nombre y apellido: ', lista[i].nomyap); writeln('Nota final: ', lista[i].notas.final:2:2); end; readkey end; procedure calculaNotaFinal(var lista: tVector; n: integer); var i: integer; begin for i:=1 to n do lista[i].notas.final:=(lista[i].notas.parcial1+lista[i].notas.parcial2)/2; end; begin {Prog. Ppal.} cantAlumnos:=0; repeat begin clrscr; writeln('1> Alta de alumno'); writeln('2> Ordena lista'); writeln('3> Calcula nota final'); writeln('4> Listado de alumnos'); writeln('5> Sale'); write('OPCION = ');

Page 77: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 77

readln(opcion); if (opcion='1') then altas(lista,cantAlumnos) else if (opcion='2') then ordena(lista,cantAlumnos) else if (opcion='3') then calculaNotaFinal(lista,cantAlumnos) else if (opcion='4') then listado(lista,cantAlumnos) end; until (opcion='5'); end. Ejemplo 3 El inventario de un almacén de artículos deportivos se desea guardar en un array de registros Artículos con los campos: nombre, número de código (seis dígitos), número de artículo y precio. Escribir un procedimiento que lea y almacene el archivo de datos del inventario en un array de registros adecuados. El programa principal debe contemplar las siguientes opciones, que serán realizadas también con procedimientos: impresión total del inventario, búsqueda de un artículo por número de código, actualización semanal (altas y bajas de artículo), ordenación alfabética por nombre y ordenación decreciente por número de artículo. Análisis: El programa se resuelve utilizando una estructura alternativa múltiple, que según la opción elegida bifurca hacia el procedimiento correspondiente. Como archivo de entrada de datos utilizaremos el teclado. PROGRAM GranAlmacen; USES CRT; CONST Maximo= 100; (*máximo número de artículos*) TYPE Rango = 1..Maximo; Rango0 = 0..Maximo; Cadena10 = String [10]; Cadena6 = String [6]; Producto = Record Nombre: Cadena10;

Page 78: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 78

Codigo: Cadena6; Precio: Real; Unidades: Integer End; Almacen = Array [Rango] of Producto; VAR Total : Rango0; Opcion : Integer; Productos : Almacen; PROCEDURE MENU; BEGIN ClrScr; Writeln (‘Menu’:42); Writeln (‘0.-’: 30, ‘SALIDA’); Writeln (‘1.-’: 30, ‘IMPRIMIR Almacen’); Writeln (‘2.-’: 30, ‘BUSCAR POR CODIGO’); Writeln (‘3.-’: 30, ‘ALTAS Y BAJAS’); Writeln (‘4.-’: 30, ‘ORDENAR POR NOMBRE’); Writeln (‘5.-’: 30, ‘ORDENAR POR CODIGO’); END; PROCEDURE PAUSA; CONST Caracteres = [#0.. #255]; VAR Letra : Char; BEGIN GotoXY (2,23); ClrEol; Write (‘Pulse una tecla para continuar’); Repeat Letra := Readkey until letra in Caracteres; END; PROCEDURE LEERARTICULO (Var Articulo: Producto); *Introduce por teclado los BEGIN datos de un artículo* With Articulo do Begin Write (‘Nombre:’:30); Readln (Nombre); Write (‘Codigo:’:30); Readln (Codigo);

Page 79: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 79

Write (‘Precio:’:30); Readln (Precio); Write (‘Unidades:’:30); Readln (Unidades) End END; PROCEDURE CREACION (Total: Rango0; Var Productos: Almacen); VAR I : Rango; *genera los datos de todos los BEGIN productos del almacén* For I:= 1 to Total do Begin ClrScr; Writeln (‘Introduzca datos del producto’:60, I:1); LeerArticulo (Productos[I]) End END; PROCEDURE ESCRIBIRARTICULO (Articulo: Producto); *muestra los datos de

cada artículo* BEGIN ClrScr; With Articulo do Begin Writeln (‘Nombre:’, Nombre); Writeln (‘Codigo:’, Codigo); Writeln (‘Precio:’, Precio); Writeln (‘Unidades:’, Unidades); Writeln End; Pausa END; PROCEDURE IMPRESION (Total: Rango0; Var Productos: Almacen); VAR I: Rango; *muestra los datos de todos los artículos del BEGIN almacén* For I:= 1 to Total do EscribirArticulo (Productos[I]); ClrScr; Writeln (‘FIN LISTADO ALMACEN’:50); Pausa END;

Page 80: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 80

PROCEDURE BUSCAR (Total: Rango0; Var Productos: Almacen; Codigo: Cadena6; Var Posicion: Rango0; Var Hallado: Boolean); VAR I: Rango0; *realiza búsqueda secuencial BEGIN de un artículo* Hallado: False; I:= 0; While not Hallado and (I < Total) do Begin I := I + 1; Hallado := Productos [I].Codigo = Codigo End; If Hallado then Posicion:= I else Posicion:= 0 END; PROCEDURE BUSCARPORCODIGO (Total: Rango0; Var Productos: Almacen); VAR Codigo: Cadena6; Posicion: Rango0; Hallado: Boolean; BEGIN ClrScr; Write (‘Introduzca Código Artículo a buscar:’); Readln (Código); Buscar (Total, Productos, Codigo, Posicion, Hallado); If Hallado then EscribirArticulo (Productos[Posicion]) else Writeln (‘No existe artículo con código’, Codigo) END; PROCEDURE INSERTAR (Total: Rango0; Var Productos: Almacen; Articulo: Producto; Lugar: Rango0); VAR *inserta un nuevo articulo en el array de I: Rango; productos en la posición indicada por la BEGIN variable lugar* For Y := Total+1 to lugar+1 do Productos[I] := Productos[I-1]; Productos[Lugar] := Articulo END; PROCEDURE ELIMINAR (Total: Rango0; Var Productos: Almacen; Lugar: Rango0); VAR I: Rango; BEGIN For I:= Lugar to Total-1 do Productos[I]:= Productos[I+1]

Page 81: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 81

END; PROCEDURE ACTUALIZAR (Var Total: Rango0; Var Productos: Almacen); VAR *Da altas y bajas en el array de productos. Modo: Char; Para ello utiliza los procedimientos internos de altas y bajas.*

PROCEDURE ALTAS (Var Total: Rango0; Var Productos: Almacen); VAR *Da altas en el array de productos* Articulo: Producto; Indice: Rango0; Hallado: Boolean; BEGIN If Total = Maximo then Writeln (‘No se pueden dar altas. Almacén Completo’) else Begin LEERARTICULO (Articulo); BUSCAR (Total, Productos, Articulo.Codigo, Indice, Hallado); If Hallado then Writeln (‘El artículo ya existe en Almacén’) else Begin INSERTAR (Total, Productos, Articulo, Indice);

Total := Total+1 End End; Pausa END; *fin procedimiento ALTAS*

PROCEDURE BAJAS (Var Total: Rango0; Var Productos: Almacen); VAR *Da bajas en el array de productos* Codigo: String[6]; Indice: Rango0; Hallado: Boolean; BEGIN If Total = 0 then Writeln (‘No se pueden dar bajas. Almacén Vacío’) else Begin Write (‘Introduzca Código de artículo a dar de baja’); Readln (Codigo);

Page 82: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 82

BUSCAR (Total, Productos, Codigo, Indice, Hallado); If Hallado then Begin ELIMINAR (Total, Productos, Indice); Total := Total-1 End

else Writeln (‘El artículo no existe’) End; Pausa END; *fin procedimiento BAJAS* BEGIN *comienzo del procedimiento ACTUALIZAR* ClrScr; Repeat Write (‘Desea altas (a-A) o bajas (b-B)’); Readln (Modo) Until Upcase (Modo) in [‘A’, ‘B’]; If Upcase (Modo) = ‘A’ then ALTAS (Total, Productos); else BAJAS (Total, Productos) END; *fin del procedimiento ACTUALIZAR* PROCEDURE CAMBIAR (Var A,B: Producto); *Intercambia dos productos* VAR Aux : Producto; BEGIN Aux := A; A := B; B := Aux END; PROCEDURE ORDENNOMBRE (Total: Rango0; Productos: Almacen); VAR *Ordena ascendentemente los productos por su nombre* I, J: Rango0; BEGIN For I:= 1 to Total-1 do

For J:= I+1 to Total do If Productos[I].Nombre > Productos[J].Nombre then

CAMBIAR (Productos[I], Productos[J]); IMPRESION (Total, Productos)

END;

Page 83: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 83

PROCEDURE ORDENCODIGO (Total: Rango0; Productos: Almacen); VAR *Ordena descendentemente los productos por su codigo* I, J: Rango0; BEGIN For I:= 1 to Total-1 do For J:= I+1 to Total do If Productos[I].Codigo < Productos[J].Codigo then CAMBIAR (Productos[I], Productos[J]); IMPRESION (Total, Productos) END; BEGIN *comienzo del procedimiento principal* ClrScr; Write (‘Escriba número de productos:’); Readln (Total); ClrScr; CREACION (Total, Productos); Repeat MENU; Repeat GotoXY (2, 23); ClrEol; Write (‘Introduzca Opción’); Readln (Opcion); Until Opcion in [0..5]; Case Opcion of 1: IMPRESION (Total, Productos); 2: BUSCARPORCODIGO (Total, Productos); 3: ACTUALIZAR (Total, Productos); 4: ORDENNOMBRE (Total, Productos); 5: ORDENCODIGO (Total, Productos); End Until Opcion = 0; ClrScr; Writeln (‘F I N D E P R O G R A M A’: 54); Pausa END. Aclaraciones: * GotoXY (x,y) Posiciona el cursor en coordenadas (x,y).

Page 84: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 84

* ClrEol Borra todos los caracteres desde la posición del cursor hasta el final de la linea. * Readkey Lee un caracter del teclado. * Upcase (letra) Convierte a mayúscula el contenido de la variable letra. * GetDate (Var Dia, Mes, Anio, DiaSemana: Word) Se encuentra en la Unidad DOS. Este procedimiento devuelve la fecha del día en números. También devuelve el día de la semana como un número que va desde 0 a 6. * GetTime (Var Horas, Minutos, Segundos, Centésimas: Word) Devuelve la hora del sist. operativo. También se encuentra en la Unidad DOS.

Page 85: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO VI

Archivos

Page 86: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 86

ARCHIVOS

El término archivo designa generalmente una colección de datos almacenados en disco o cinta. Pascal emplea la noción de archivo para indicar una secuencia de elementos del mismo tipo, llamados registros, de forma que sólo uno de los componentes de la estructura es accesible en un instante determinado, restringiendo Pascal el acceso a los elementos del archivo (registros) al modo secuencial. Dadas las limitaciones del procesamiento secuencial Turbo Pascal suministra procedimientos para tratamiento de archivos de acceso directo o aleatorio. Concepto general Un archivo es una estructura de datos consistente en una secuencia de elementos o componentes llamados registros, todos del mismo tipo, ya sea simple o estructurado. A diferencia de los arrays, un archivo puede almacenarse en un dispositivo auxiliar (discos, cintas, etc.), de forma que los datos obtenidos antes, durante y después del procesamiento de los datos no se pierden. Declaración y apertura de archivos Para declarar una variable archivo es necesario indicar el tipo de elemento que lo compone, es decir, definir la naturaleza de sus registros. Esta declaración puede hacerse directamente en la sección de declaración de variables, o declarar previamente el tipo de archivo en la sección de tipos y posteriormente la variable o variables correspondientes en la sección de variables, procedimiento éste más aconsejado. Las dos declaraciones siguientes son equivalentes:

VAR ENTEROS: File of Integer;

TYPE NUMEROS = File of Integer VAR ENTEROS: NUMEROS;

Page 87: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 87

Pasaje de archivos por parámetro El paso de parámetros de tipo archivo o archivo ha de hacerse siempre por referencia (por variable). No se puede, en ningún caso, pasarlos por valor, por tanto, en la cabecera de subprogramas deben ir siempre precedidos por la palabra reservada VAR. Ejemplo: PROCEDURE PROCESAR (VAR Numeros: TipoReales); o bien FUNCTION SUMA (VAR Numeros: TipoReales) : Real; Procedimiento ASSIGN El procedimiento ASSIGN no forma parte de Pascal estándar. Simplemente, se utiliza por los compiladores con objeto de asociar un archivo lógico con su archivo físico correspondiente. Después de la asignación, cualquier operación sobre la variable archivo afectará al archivo DOS correspondiente. El formato del procedimiento es: ASSIGN (VarArch, NomArch); donde VarArch es una variable de tipo archivo y NomArch una variable tipo cadena o una cadena de caracteres que contiene el nombre del archivo físico asociado, con expresión de unidad de disco, ruta de acceso y nombre con su extensión del archivo. Ejemplo: ASSIGN (Pruebas, ‘C:\TURBO/FACULTAD/INGENIERIA/ALGORIT.DAT’); o bien VAR NomArch: String; BEGIN Write (‘Introduzca nombre DOS del archivo:’); Readln (NomArch); Assign (VarArch, NomArch); ..... introduciéndose por teclado: C:\TURBO\FACULTAD\INGENIERIA\ALGORIT.DAT Procedimiento RESET El procedimiento RESET abre un archivo para lectura posicionando el puntero de lectura del archivo en el primer elemento del archivo y poniendo la variable booleana EOF (marca de fin de archivo) asociada al archivo en false, o en la marca de fin de archivo si el archivo está vacío, en cuyo caso la variable booleana EOF toma el valor true. Puede utilizarse el

Page 88: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 88

procedimiento RESET sobre archivos que ya estén abiertos para lectura siendo el resultado de la operación reposicionar el puntero de lectura en el primer registro del archivo. RESET abre el archivo para lectura, por lo que no permite la modificación del contenido de ningún registro. El formato del procedimiento RESET es: RESET (NomArch) donde NomArch es el identificador o nombre de la variable archivo. Procedimiento REWRITE El procedimiento REWRITE abre el archivo para escritura destruyendo el contenido del archivo si la operación se realiza sobre un archivo que ya existe. No es posible leer los datos correspondientes a los registros de un archivo que está abierto con el procedimiento REWRITE. El formato del procedimiento REWRITE es: REWRITE (NomArch) Procedimiento CLOSE Una de las diferencias más acusadas en el tratamiento de archivos entre Pascal estándar y Turbo Pascal es la necesidad de indicar con el procedimiento CLOSE el proceso de cierre de archivos. En Pascal estándar los archivos se cierran cuando finaliza el programa, mientras que en Turbo Pascal y otros compiladores es necesario el empleo explícito del procedimiento CLOSE, porque de lo contrario los últimos datos se perderán. El empleo del procedimiento CLOSE es necesario tanto en procesos de lectura como en procesos de escritura sobre archivos. La sintaxis o formato del procedimiento CLOSE es: CLOSE (NomArch) Procedimiento READ Se utiliza para introducir el contenido de un registro del archivo en una variable de memoria definida del mismo tipo de dato que el registro leído. El formato del procedimiento es: READ (NomArch, NomReg)

Page 89: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 89

Procedimiento WRITE WRITE escribe en un registro del archivo el contenido de una variable de memoria definida del mismo tipo. El formato del procedimiento es: WRITE (NomArch, NomReg) Función EOF (marca de fin de archivo) El tratamiento de una estructura repetitiva como es el caso de un archivo secuencial requiere detectar la marca de fin de archivo, con objeto de evitar errores en tiempo de ejecución, al intentar leer datos que no existen. En cada iteración del bucle se lee un registro del archivo avanzando el puntero de lectura del archivo una posición hacia el final del archivo. En la lectura del último registro el salto o avance del puntero posiciona éste sobre la marca de fin de archivo colocando la función lógica EOF (End Of File) asociada a cada archivo a verdadero (True). La verificación del estado de EOF se suele realizar con un bucle WHILE o bien mediante una sentencia IF. La función tiene el siguiente formato: EOF (NomArch) Dado que un archivo puede estar inicialmente vacío el tratamiento típico de cualquier archivo sería: While Not EOF (Gustavo) do Begin READ (Gustavo, Info); Escribir (Info) *procedimiento que muestra el registro* End; donde Info es una variable registro del mismo tipo que los componentes del archivo y Gustavo es el archivo que se procesa.

ARCHIVOS DE TEXTO Los archivos de texto, también llamados archivos ASCII, están constituidos por secuencias de caracteres de longitud variable separadas unas de otras por los códigos retorno de carro/avance de línea (CR/LF o bien 13/10 en ASCII). Constituyen un tipo predefinido en Pascal, que se designa por Text, tipo que no tiene que ser declarado de forma explícita en la sección TYPE. Un archivo de texto se termina con una marca fin de archivo EOF (^Z o el código ASCII 26).

Page 90: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 90

La declaración de un archivo de texto se hace mediante el siguiente formato general: VAR NomArch: Text; Turbo Pascal trata algunos dispositivos como archivos de texto, asignándoles nombres de al menos tres caracteres. En la siguiente tabla se muestra una lista de dispositivos con los identificadores asociados.

NOMBRE DESCRIPCIÓN ‘LPT1’ Impresora 1 ‘LPT2’ Impresora 2 ‘LPT3’ Impresora 3 ‘PRN’ Impresora (LPT1)

Ejemplo: PROGRAM FACULTAD; VAR Impresora: Text; BEGIN Assign (Impresora, ‘PRN’); Rewrite (Impresora); Writeln (Impresora, ‘Esta frase se imprime’); Writeln (‘Esta frase no se imprime, se visualiza en pantalla’); Close (Impresora) END. La impresora es un periférico que se puede tratar como un archivo de texto. Este periférico puede asignarse a los dispositivos mencionados anteriormente o bien utilizar la unidad Printer que proporciona Turbo Pascal y que asigna por defecto al dispositivo LPT1 el nombre lógico de archivo LST. Procedimientos WRITE y WRITELN Write y Writeln escriben el valor de una o varias variables en un archivo de texto de la misma forma que lo hacen sobre pantalla. Los formatos son: WRITE (NomArch, variable1, variable2, ... , variableN); WRITELN (NomArch, variable1, variable2, ... , variableN);

Page 91: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 91

Si no se especifica una variable de archivo de texto en las sentencias, la salida es sobre la pantalla. La sentencia WRITELN, que sólo puede utilizarse con archivos de texto, inserta una marca de fin de línea después de la última variable escrita. Procedimientos READ y READLN Las sentencias READ y READLN leen caracteres de un archivo igual que lo hacen desde teclado. Los formatos de las sentencias son: READ (NomArch, variable1, variable2, ... , variableN); READLN (NomArch, variable1, variable2, ... , variableN); Los procedimientos READLN y WRITELN sólo pueden utilizarse con archivos texto. Sirven para generar en escritura (Writeln) y detectar en lectura (Readln) los caracteres de control (CR/LF) que sirven para separar las distintas líneas de un archivo de texto. No deben asignarse a variables de tipo char los caracteres fin de línea, ya que éstos no son más que elementos para separar la información, y no información propiamente dicha. Observación: Turbo Pascal, no así Pascal estándar, permite leer una cadena de caracteres de un archivo de texto. Si el número de caracteres de una línea de texto es inferior a la longitud de la cadena a leer, la lectura no continúa con los caracteres de la línea siguiente, siendo, por tanto, la longitud lógica de la cadena el número de caracteres leídos. Función EOLN EOLN (NomArch) es una función lógica o booleana que se asocia a cada archivo de texto para su procesamiento. Toma el valor True bajo dos condiciones: cuando el puntero de lectura del archivo encuentra un fin de linea o cuando el puntero de lectura del archivo alcanza el final del archivo. El uso típico de EOLN es detección en lectura de la marca fin de línea Procedimientos APPEND A diferencia de Pascal, Turbo Pascal proporciona un procedimiento para añadir datos al final de un archivo de texto que ya existe. Para ello se debe utilizar el procedimiento APPEND que actúa como RESET abriendo un archivo, pero se posiciona al final del archivo en condiciones de aceptar más datos. Ejemplo: VAR f: Text; BEGIN Assign (f, ‘Frases.Dat’);

Page 92: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 92

Append (f); Writeln (f, ‘7502 Algoritmos y Programación I’); Writeln (f, ‘Facultad de Ingeniería’); Close (f) END. Ejemplo: PROGRAM HacerCopia; USES DOS, CRT; VAR Origen, Copia: Text; Car: Char; BEGIN ClrScr; Assign (Origen, ‘C:\Datos\Texto.Dat’); Reset (Origen); Assign (Copia, ‘C:\Datos\Copiex.Dat’); Rewrite (Copia); While Not EOF (Origen) do Begin While Not EOLN (Origen) do Begin Read (Origen, Car); Write (Copia, Car) End; *se llegó a un fin de línea en archivo Origen* Writeln (Copia); *se escribe fin de línea en Copia* Readln (Origen) *siguiente línea de archivo Origen* End; Close (Origen); Close (Copia) END.

ARCHIVOS DE ACCESO DIRECTO (Con Tipos) Están formados por registros del mismo formato y longitud por lo que permiten el acceso a un registro específico mediante un número asociado al mismo, que se denomina su número de registro lógico. El número asociado es de tipo LongInt (entero largo) y se asigna al primer registro lógico el valor 0. Para que un archivo pueda ser tratado por posicionamiento o acceso directo debe residir obligatoriamente en un dispositivo de almacenamiento de ese tipo.

Page 93: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 93

La declaración de un archivo de acceso directo es idéntica a la de otros archivos y sólo se distingue de ellos por las funciones de posicionamiento en registro. Un puntero del archivo memoriza el número del registro correspondiente para una operación de lectura o escritura. Cuando un archivo se abre su puntero indica el registro número 0, es decir el primer registro. Después de cada operación de lectura o escritura, el puntero de archivo incrementa en uno el número de registro leído o escrito. El contenido de un archivo de acceso directo se almacena en disco bajo forma binaria comprimida y no es visualizable directamente en pantalla, como los archivos de texto, con la orden TYPE de DOS o con editores. Apertura de un archivo La operación de apertura de un archivo se realiza con el procedimiento REWRITE, para abrir un archivo nuevo, y el procedimiento RESET, para abrir un archivo existente. REWRITE (Alumnos); (*el archivo puede existir*) RESET (Alumnos); (*el archivo debe existir*) Un archivo de acceso directo está siempre abierto para lectura y escritura independientemente de la sentencia utilizada para abrir el archivo. Si se utiliza REWRITE, toda versión precedente con igual nombre se borrará y perderá su contenido. Tenga prudencia en la creación de un archivo con la ayuda de este procedimiento. El procedimiento APPEND no se aplica en archivos tipeados, ya que no es necesario. Tamaño de un archivo La función predefinida FILESIZE devuelve el tamaño del archivo en formato de entero largo (LongInt), indicando el número de registros almacenados. Esta función toma el nombre del archivo como único argumento. Si se trata de un archivo vacío FileSize devuelve el valor cero. Tamanio := FILESIZE (Alumnos) Registro actual en un archivo La función predefinida FILEPOS devuelve en formato de entero largo el número del registro actual. Toma como argumento la variable archivo. RegActual := FILEPOS (Alumnos) Posicionamiento en un archivo

Page 94: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 94

El procedimiento SEEK permite seleccionar un registro específico del archivo por su número de registro, para su uso en una operación de lectura o escritura. Su formato es: SEEK (Alumnos, NumReg) En realidad Pascal lo que hace es desplazar el puntero del archivo al registro número NumReg y posicionarse en el mismo. SEEK (Alumnos, 0); *primer registro* SEEK (Alumnos, FileSize(Alumnos) -1); *último registro* SEEK ( Alumnos, FileSize (Alumnos)); *añade al final* Lectura y escritura de archivos Los procedimientos utilizados para leer y escribir registros en un archivo de acceso directo son READ y WRITE. La operación de lectura o escritura se realizará sobre el registro actual. Para hacerlo sobre uno determinado hay que emplear previamente la función de posicionamiento SEEK. READ (Alumnos, Registros); WRITE (Alumnos, Registro); En cada operación de lectura o escritura el puntero asociado al archivo avanza al siguiente registro. Cierre de un archivo El cierre de un archivo de acceso directo se efectúa de igual modo que con cualquier otro archivo. CLOSE (Alumnos);

OBSERVACIÓN: Turbo Pascal tiene predefinidos una serie de constantes y tipos enteros, que se podrían definir en Pascal estándar del siguiente modo: CONST MaxInt = 32767; MaxLongInt = 2147483647; TYPE Byte = 0 .. 255;

Page 95: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 95

Word = 0 .. 65535; ShortInt = -128 .. 127; Integer = -Maxint-1 .. Maxint; LongInt = -MaxLongInt-1 .. MaxLongInt;

Page 96: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 96

ARCHIVOS SECUENCIALES (EJEMPLOS) Ejemplo 1 Se cuenta con un archivo secuencial llamado ENTEROS.DAT, cuyos elementos son números enteros. Se pide un programa que:

- Abra el archivo para lectura. - Lea el primer registro. - Imprima dicho registro. - Cierre el archivo.

NOTA: en este caso estamos utilizando la palabra ‘registro’ para referirnos a una variable ‘atómica’ (ya que los elementos del archivo son números enteros). program enteros; type tArchivo = file of integer; {Declaracion del tipo de archivo} var numeros: tArchivo; {numeros es el nombre logico del archivo} num: integer; begin assign(numeros,'ENTEROS.DAT'); {ENTEROS.DAT es el nombre fisico} reset(numeros); {del archivo} read(numeros,num); write(num); close(numeros); end. Ejemplo 2 Abrir el archivo llamado ALUMNOS.DAT (ó crearlo si no existe), cuyos registros contengan los siguientes campos:

- Nombre y apellido (string). - DNI (longint). - Padrón (longint).

Se pide un programa que cargue en un registro los datos de un solo alumno, y luego grabe dicho registro en el archivo.

Page 97: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 97

program alumnos; type tRegistro = record nomyap: string; dni: longint; padron: longint; end; tArchivo = file of tRegistro; var alum: tArchivo; alumno: tRegistro; begin assign(alum,'ALUMNOS.DAT'); {$I-} reset(alum); {$I+} if (ioresult<>0) then rewrite(alum); alumno.nomyap:='Juan Perez'; alumno.dni:=23000460; alumno.padron:=71000; write(alum,alumno); close(alum); end. Ejemplo 3 Crear un archivo binario (secuencial) llamado LETRAS.DAT cuyos elementos sean caracteres. Se pide un programa que:

- Cree el archivo. - Copie en el archivo la frase ‘Hoy es un lindo día’. - Lea uno por uno los caracteres del archivo y los muestre por pantalla. - Cierre el archivo.

El copiado de los caracteres se debe hacer dentro de un procedimiento. program frase; type tArchivo = file of char;

Page 98: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 98

var letras: tArchivo; cadena: string; procedure copia(var arch: tArchivo; cadena: string); var i: integer; begin reset(arch); for i:=1 to 19 do write(arch,cadena[i]); end; procedure imprime(var arch: tArchivo); var caracter: char; begin reset(arch); while (not eof(arch)) do begin read(arch,caracter); write(caracter); end; end; begin {Prog. Ppal.} assign(letras,'LETRAS.DAT'); rewrite(letras); cadena:='Hoy es un lindo dia'; copia(letras,cadena); imprime(letras); close(letras); end. Ejemplo 4 Llegado fin de mes, la empresa para la cual trabajamos nos solicita un programa que sea capáz de liquidar los sueldos. Es decir, debe emitir un informe indicando de cada empleado los siguientes datos: - Nombre y apellido (string) - Nro. DNI (longint)

Page 99: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 99

- Sueldo basico (real) - Retenciones (real) - Sueldo neto (real) Debe proveerse también de un procedimiento para la carga del archivo. Los datos para cada empleado dentro de dicho archivo son: - Código de empleado (0..N-1, con N la cant. de empleados) - Nombre y apellido (string) - Nro. DNI (longint) - Sueldo basico (real) - Retenciones (real) program liquidacion; uses wincrt; type tRegistro = record codigo: integer; nomyap: string[30]; DNI: longint; sueldo_basico: real; reten: real; end; tArchivo = file of tRegistro; var empleados: tArchivo; opcion: char; procedure carga(var arch: tArchivo); var empleado: tRegistro; opcion: char; begin clrscr; rewrite(arch); repeat begin with empleado do begin write(‘Código de empleado: ‘);

Page 100: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 100

readln(codigo); write('Nombre y apellido: '); readln(nomyap); write('DNI: '); readln(DNI); write('Sueldo basico: '); readln(Sueldo_basico); write('Retenciones: '); readln(reten); write('Otro empleado? (s/n): '); readln(opcion); writeln; end; write(arch,empleado); end; until upcase(opcion)='N'; end; procedure liquida(var arch: tArchivo); var empleado: tRegistro; begin clrscr; reset(arch); while not eof(arch) do begin read(arch,empleado); with empleado do begin writeln(‘Código de empleado: ‘, codigo); writeln('Nombre y apellido: ', nomyap); writeln('DNI: ', DNI); writeln('Sueldo básico: ', sueldo_basico:5:2); writeln('Retenciones: ', reten:5:2); writeln('Sueldo neto: ', sueldo_basico - reten:5:2); end; write('Presione una tecla para continuar...'); readkey; writeln; end; end; begin {Prog. Ppal.}

Page 101: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 101

assign(empleados,'EMPLEADO.DAT'); {$I-} reset(empleados); {$I+} if (ioresult<>0) then rewrite(empleados); repeat begin clrscr; writeln('1> CARGA'); writeln('2> LIQUIDA'); writeln('3> SALE'); write('OPCION ?: '); readln(opcion); if (opcion='1') then carga(empleados) else if (opcion='2') then liquida(empleados); end; until (opcion='3'); close(empleados); end. Ejemplo 5 Crear un archivo secuencial Agenda cuyos registros contienen los campos: Nombre, Apellido, Edad, Calle, Código Postal y Ciudad. PROGRAM Crear_Archivo_Secuencial; TYPE Direcciones = Record Nombre: String [20]; Apellido: String [20]; Edad: Integer; Calle: String [20]; CodPostal: Integer; Ciudad: String [20]; End; Archivo = File of Direcciones; VAR Agenda : Archivo; Articulo: Direcciones;

Page 102: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 102

MasDatos: Char; BEGIN Assign (Agenda, ‘C:\Datos\Personas.Dat’); Rewrite (Agenda); Repeat With Articulo do Begin Write (‘Nombre:’); Readln (Nombre); Write (‘Apellido:’); Readln (Apellido); Write (‘Edad:’); Readln (Edad); Write (‘Calle:’); Readln (Calle); Write (‘C. Postal:’); Readln (CodPostal); Write (‘Ciudad:’); Readln (Ciudad) End; Write (Agenda, Articulo); Write (‘¿ Otra Dirección ? (S/N)’); Readln (MasDatos); Until Upcase (MasDatos) = ‘N’; Close (Agenda) END. Ejemplo 6 Escribir un bloque de código para mostrar el archivo Agenda.

Reset (Agenda); While not Eof (Agenda) do Begin Read (Agenda, Articulo); With Articulo do Begin Writeln (‘Nombre:’, Nombre); Writeln (‘Apellido:’, Apellido); Writeln (‘Edad:’, Edad); Writeln (‘Calle:’, Calle); Writeln (‘C. Postal:’, CodPostal); Writeln (‘Ciudad:’, Ciudad); End End;

Page 103: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 103

Close (Agenda);

Ejemplo 7 Escribir un programa que busque en un archivo de enteros y encuentre el entero más grandes y el más chico del archivo. Debe informarse, además, la cantidad de apariciones de dichos números en el archivo. ANALISIS: Para encontrar el número más grande y el más chico se procede de la siguiente forma: * Se abre el archivo para lectura. * Si el archivo no está vacío, el primer registro o número será, momentáneamente, el mayor y el menor elemento del archivo. * Con una estructura repetitiva del tipo ‘Mientras no sea fin de archivo’ se van leyendo los números del archivo y comparando con el mayor y menor número obtenido hasta el momento. Si el número leído es mayor que el mayor actual, el mayor actual pasará a ser el número leído. De la misma forma se procede para determinar si es menor que el menor actual. * Después de procesado el archivo se escriben los números Mayor y Menor. PROGRAM Busqueda_Enteros; USES Dos, Crt; TYPE Archivo = File of Word; VAR Enteros: Archivo; Mayor, Menor: Word; PROCEDURE GenerarEnteros (Var f: Archivo); VAR Numero: Word Numeros, Contador: Byte; BEGIN Rewrite (f); Randomize; Numeros := Random (200); For Contador := 1 to Numeros do Begin Numero := Random (1000); Write (numero: 5); Write (f, numero)

Page 104: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 104

End; Close (f); Writeln (‘Total de números generados:’, Numeros) END; PROCEDURE MayorMenor (Var f: Archivo; Var Mayor, Menor: Word) VAR Numero: Word; VecesMayor, VecesMenor: Byte; BEGIN Reset (f); If not EOF (f) then Begin Read (f, numero); Mayor := numero; VecesMayor := 1; Menor := numero; VecesMenor := 1; While not EOF (f) do Begin Read (f, numero); If numero > Mayor then Begin Mayor := numero; VecesMayor := 1 End else If numero = Mayor then VecesMayor:= VecesMayor + 1; If numero < Menor then Begin Menor := numero; VecesMenor := 1 End else If numero = Menor then VecesMenor := VecesMenor + 1; End End; Close (f); Writeln (‘Mayor:’, Mayor, ‘y aparece:’, VecesMayor, ‘veces’); Writeln (‘Menor:’, Menor, ‘y aparece:’, VecesMenor, ‘veces’);

Page 105: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 105

END; BEGIN ClrScr; Assign (Enteros, ‘C:\Enteros.Dat’); GenerarEnteros (Enteros); MayorMenor (Enteros, Mayor, Menor) END. Ejemplo 8 Se dispone de un archivo de inventario STOCK , que contiene los siguientes campos: Nombre, Código, Cantidad, Precio, Fabricante). Escribir un programa que busque un determinado artículo por el número de código. Si el artículo existe, visualizar nombre y código; en caso contrario, se emite un mensaje por la falta. ANALISIS: El ejercicio consiste en realizar una búsqueda secuencial en un archivo de datos desordenado. La búsqueda se terminará cuando se encuentre el dato buscado o cuando se llegue a final de archivo. PROGRAM DEPOSITO; USES Dos, Crt; TYPE Cadena30 = String [30]; Cadena6 = String [6]; RegArticulo = Record Nombre: Cadena30; Fabricante: Cadena6; Precio: Real; Cantidad: Byte End; Archivo = File of RegArticulo; VAR Stock : Archivo; Masconsultas : Char; PROCEDURE Pausa; CONST

Page 106: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 106

Caracteres = [#0 .. #255]; VAR Letra : Char; BEGIN GotoXY (2,23); ClrEol; Write (‘Pulse una tecla para continuar’); Repeat Letra := Readkey until letra in Caracteres; END; PROCEDURE CrearArticulo (Var Articulo : RegArticulo); BEGIN ClrScr; With Articulo do Begin Write (‘Introduzca Nombre:’); Readln (Nombre); Write (‘Introduzca Fabricante:’); Readln (Fabricante); Write (‘Introduzca Código:’); Readln (Codigo); Write (‘Introduzca Precio:’); Readln (Precio); Write (‘Introduzca Cantidad:’); Readln (Cantidad); End END; PROCEDURE MostrarArticulo (Var Articulo: RegArticulo); BEGIN ClrScr; With Articulo do Begin Write (‘Nombre: ’, Nombre); Write (‘Fabricante:’, Fabricante); Write (‘Código:’, Codigo); Write (‘Precio:’, Precio); Write (‘Cantidad:’, Cantidad); End END; PROCEDURE GenerarArchivo (Var f: Archivo);

Page 107: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 107

VAR MasDatos: Char; Articulo : RegArticulo; BEGIN Rewrite (f); Repeat CrearArticulo (Articulo); Write (f, Articulo); GotoXY (2,23); ClrEol; Write (‘Más Articulos (s-n):’); Readln (MasDatos) Until Upcase (Masdatos) <> ‘S’; Close (f) END; PROCEDURE Consultar (Var f: Archivo); VAR Hallado: Boolean; Articulo: RegArticulo; Codigo: Cadena30; BEGIN ClrScr; Write (‘Introduzca Código Articulo:’); Readln (Codigo); Reset (f); Hallado := False; While not EOF (f) and not Hallado do Begin Read (f, articulo); Hallado := Código = Articulo.Codigo End; If Hallado then MostrarArticulo (Articulo) else Begin Writeln (‘No existe el código’, Codigo, ‘en depósito’); Pausa End END; BEGIN ClrScr; Assign (Stock, ‘C:\Stock.Dat’); GenerarArchivo (Stock); Repeat Consultar (Stock); GotoXY (2,23);

Page 108: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 108

ClrEol; Write (‘Mas consultas: (s-n)’); Readln (MasConsultas) Until Upcase (MasConsultas) <> ‘S’ END. Ejemplo 9 Escribir un programa que permita crear un archivo inventario de los libros de una librería, así como calcular e imprimir el valor total del inventario. Campos del registro: título, autor, número de código, precio, cantidad. ANALISIS: El programa utiliza dos procedimientos: uno para generar el archivo de libros, y otro para calcular el valor total de estos y mostrar sus datos. El procedimiento que genera los libros de la librería llama a otro procedimiento para introducir los datos de cada uno de los volúmenes. Tanto este procedimiento como el de valoración son procedimientos estándar de tratamiento de archivos secuenciales. PROGRAM GestionLibreria; USES Dos, Crt; TYPE Cadena30 = String [30]; Cadena6 = String [6]; Reglibro = Record Titulo: Cadena30; Autor: Cadena30; Codigo: Cadena6; Precio: Word; Unidades: Byte End; Libreria = File of Reglibro; VAR Libros : Libreria; Total : LongInt; PROCEDURE CrearLibro (Var L: Reglibro); BEGIN ClrScr; With L do Begin Write (‘Introduzca Titulo’);

Page 109: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 109

Readln (Titulo); Write (‘Introduzca Autor’); Readln (Autor); Write (‘Introduzca Codigo’); Readln (Codigo); Write (‘Introduzca Precio’); Readln (Precio); Write (‘Introduzca Unidades’); Readln (Unidades); End END; PROCEDURE GenerarArchivo (Var F: Libreria); VAR MasDatos : Char; Libro : Reglibro; BEGIN Rewrite (F); Repeat CrearLibro (Libro); Write (F, Libro); GotoXY (2,23); ClrEol; Write (‘Mas Libros (s-n)’); Readln (MasDatos); Until Upcase (MasDatos) <> ‘S’; Close (F); END; PROCEDURE ValorTotal (Var F: Libreria; Var Valor: LongInt);

*Determina el valor total de la VAR librería y muestra los datos de Libro: RegLibro; cada libro* Costo: LongInt; BEGIN ClrScr; Writeln (‘Desglose importe de libros’ : 52); Writeln; Writeln (‘Titulo’: 21, ‘Unidades’: 24, ‘Precio’: 10, ‘Total’: 15); Valor := 0; Reset (F); While not EOF (F) do Begin Read (F, Libro); Costo := Libro.Unidades * Libro.Precio; With Libro do

Page 110: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 110

Write (Titulo: 35, Unidades: 10, Precio: 10); Writeln (Costo: 15); Valor := Valor + Costo End; Close (F) END; BEGIN ClrScr; Assign (Libros, ‘C:\Libros.Dat’); GenerarArchivo (Libros); ValorTotal (Libros, Total); Writeln (‘Valor Total Librería:’, Total) END. Ejemplo 10 El programa anterior se desea ampliar en el sentido de visualizar el archivo completo, o bien consultar un registro determinado. ANALISIS: Es un programa típico de procesamiento secuencial. El programa solicita del usuario si mostrará todos los datos de la librería o los de un libro en particular. Para ello utiliza una estructura alternativa doble, y dependiendo de la opción elegida bifurcará hacia un procedimiento u otro. Si la opción elegida es un listado general, se abre el archivo de libros para lectura, y mientras no se alcance el final del archivo, se leerán los datos de un libro y se mostrarán en pantalla. Si la opción es obtener los datos de un libro, se procederá de forma análoga, procesándose el archivo o bien hasta que se encuentre el libro buscado o hasta que se llegue el final del archivo, ya que el archivo de libros no está ordenado por título o por otro campo. PROGRAM GestionLibreria; USES Dos, Crt; TYPE Cadena30 = String [30]; Cadena6 = String [6]; RegLibro = Record Titulo : Cadena30; Autor : Cadena30; Codigo : Cadena6; Precio : Word;

Page 111: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 111

Unidades: Byte End; Libreria = File of RegLibro; VAR Libros : Libreria; MasConsultas : Char; PROCEDURE Pausa; CONST Caracteres = [#0 .. #255]; VAR Letra : Char; BEGIN GotoXY (2,23); ClrEol; Write (‘Pulse una tecla para continuar’); Repeat Letra := Readkey until letra in Caracteres; END; PROCEDURE MostrarLibro (Var L: RegLibro); BEGIN ClrScr; With L do Begin Writeln (‘Titulo:’, Titulo); Writeln (‘Autor:’, Autor); Writeln (‘Codigo:’, Codigo); Writeln (‘Precio:’, Precio); Writeln (‘Unidades:’, Unidades); End END; PROCEDURE ListadoTotal (Var F: Libreria); VAR Libro: RegLibro; BEGIN Reset (F); While Not EOF (F) do Begin Read (F, Libro); MostrarLibro (Libro); Pausa End

Page 112: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 112

END; PROCEDURE ConsultarLibro (Var F: Libreria); *busca un libro por su título* VAR Hallado: Boolean; Libro: RegLibro; Titulo: Cadena30; BEGIN ClrScr; Write (‘Introduzca titulo a consultar:’); Readln (Titulo); Reset (F); Hallado := False; While not EOF (F) and not Hallado do Begin Read (F, Libro); Hallado := Titulo = Libro.Titulo End; If Hallado then MostrarLibro (Libro) else Writeln (‘No existe el libro’, Titulo, ‘en la libreria’); Close (F); Pausa END; PROCEDURE Consultar (Var F: Libreria); CONST Opciones = [‘A’, ‘B’]; VAR Opcion = char; BEGIN ClrScr; Repeat Write (‘Consultar libro (A) o listar librería (B):’); Readln (Opcion) Until Opcion in opciones; If Opcion = ‘A’ then ConsultarLibro (F) else ListadoTotal (F) END; BEGIN ClrScr; Assign (Libros, ‘C:\Libros.Dat’); Repeat Consultar (Libros);

Page 113: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 113

GotoXY (2, 23); ClrEol; Write (‘Mas consultas: (s-n)’); Readln (MasConsultas) Until Upcase (MasConsultas) <> ‘S’ END. Ejemplo 11 El programa de la librería se desea ampliar para cerrar una base de datos, de modo que pueda responder correctamente a las siguientes preguntas: * ¿ Cuántos libros del autor X existen en stock ? * ¿ Cuántos libros tienen un precio entre $ 35 y $40 ? * ¿ Cuál es el número de código de “La Metamorfosis” ? * ¿ Cuántos libros del stock hay de precio mayor de $ 50 y más de 10 ejemplares ? * ¿ Cuál es el libro más vendido ? ANALISIS: Programa similar a ejercicios anteriores, excepto que en el prceso secuencial de búsqueda se cambian las condiciones de la misma, según el tipo de información requerida. PROGRAM ConsultasLibreria; USES Dos, Crt; TYPE Cadena30 = String [30]; Cadena6 = String [6]; RegLibro = Record Titulo : Cadena30; Autor : Cadena30; Codigo : Cadena6; Precio : Word; Unidades: Byte End; Libreria = File of RegLibro; VAR Libros : Libreria; Autor : Cadena30; Titulo : Cadena30;

Page 114: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 114

Maximo : Byte; Minimo : Byte; Mayor : Byte; Ejemplares : Byte; PROCEDURE Pausa; CONST Caracteres = [#0 .. #255]; VAR Letra : Char; BEGIN GotoXY (2,23); ClrEol; Write (‘Pulse una tecla para continuar’); Repeat Letra := Readkey until letra in Caracteres; END; FUNCTION LibrosAutor (Var F: Libreria; Autor: Cadena30): Byte; VAR *indica la cantidad de títulos de un Total : Byte; autor determinado* Libro : RegLibro; BEGIN Total := 0; Reset (F); While not EOF (F) do Begin Read (F, Libro); If Libro.Autor = Autor then Total := Total+1 End; Close (F); LibrosAutor := Total END; FUNCTION LibrosPrecio (Var F: Libreria; Maximo, Minimo: Byte): Byte; VAR *libros con precio comprendido entre Total : Byte; máximo y mínimo* Libro : RegLibro; BEGIN Total := 0; Reset (F); While not EOF (F) do

Page 115: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 115

Begin Read (F, Libro); With Libro do If (Precio <= Maximo) and (Precio >= Minimo) then Total := Total+1 End; Close (F); LibrosPrecio := Total END; FUNCTION CodTitulo (Var F: Libreria; Titulo: Cadena30): Cadena6; VAR *proporciona el código de un título especificado* Libro : RegLibro; BEGIN CodTitulo : ‘No hay’; Reset (F); While not EOF (F) do Begin Read (F, Libro); If Libro.Titulo = Titulo then CodTitulo := Libro.Codigo End; Close (F) END; FUNCTION LibrosPrecioUnidades (Var F: Libreria; Precio: Byte; Ejemplares: Byte): Byte; VAR *indica número de títulos con precio superior Libro : RegLibro; al indicado (precio) y con stock superior a un Total : Byte; número de ejemplares* BEGIN ClrScr; Total := 0; Reset (F); While not EOF (F) do Begin Read (F, Libro); If (Libro. precio > Precio) and (Libro.unidades > Ejemplares) then Total := Total+1 End; Close (F); LibrosPrecioUnidades := Total END; PROCEDURE ListadoTotal (Var F: Libreria); VAR

Page 116: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 116

Libro: RegLibro; BEGIN Write (‘Titulo’: 13, ‘Autor’: 20, ‘Codigo’: 10, ‘Precio’: 10, ‘Unidades’: 10); Writeln; Reset (F); While Not EOF (F) do Begin Read (F, Libro); With Libro do Writeln (Titulo: 20, Autor: 20, Codigo: 10, Precio: 10, Unidades: 10) End; Close (F) END; BEGIN *programa principal* ClrScr; Assign (Libros, ‘C:\Libros.Dat’); ListadoTotal (Libros); Write (‘Introduzca Autor:’); Readln (Autor); Writeln (Autor, ‘tiene’, LibrosAutor (Libros, Autor), ‘títulos’); Writeln; Write (‘Introduzca Precio Mínimo:’); Readln (Minimo); Write (‘Introduzca Precio Máximo:’); Readln (Maximo); Write (‘Cant. libros entre’, Maximo, ‘y’, Minimo, ‘es’, LibrosPrecio (Libros, Maximo, Minimo)); Writeln; Write (‘Introduzca un título de libro:’); Readln (Titulo); Writeln (‘Código de’, Titulo, ‘:’ , CodTitulo (Libros, Titulo)); Writeln; Write (‘Introduzca precio mayor: $’); Readln (Mayor); Write (‘Introduzca máximo número de ejemplares:’); Readln (Ejemplares); Write (‘El número de ejemplares con precio mayor que’, Mayor); Write (‘ y con más de’, Ejemplares, ‘ejemplares es’); Writeln (LibrosPrecioUnidades (Libros, Mayor, Ejemplares))

Page 117: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 117

END.

ARCHIVOS DIRECTOS (EJEMPLOS) Ejemplo 1 Crear un archivo directo llamado NUMEROS.DAT cuyos elementos sean enteros. Se pide un programa que:

- Cree el archivo. - Almacene en la posición 10 el número 250. - Cierre el archivo.

program enteros; type tArchivo = file of integer; var numeros: tArchivo; num: integer; begin assign(numeros,'NUMEROS.DAT'); rewrite(numeros); seek(numeros,10); num:=250; write(numeros,num); close(numeros); end. Ejemplo 2 Abrir el archivo STOCK.DAT, cuyos registros contienen los siguientes campos:

- Código (entero). - Descripción (string). - Precio unitario (real).

Se pide un programa que:

Page 118: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 118

- Lea el registro de la posicion 6. - Se posicione en el último registro válido del archivo (anterior al EOF). - Devuelva el número de posición de ese último registro. - Devuelva la cantidad de registros que tiene el archivo. - Cierre el archivo.

program mercaderias; type tRegistro = record codigo: integer; descr: string; punitario: real; end; tArchivo = file of tRegistro; var stock: tArchivo; producto: tRegistro; begin assign(stock,'STOCK.DAT'); reset(stock); seek(stock,6); {Se posiciona en el 7mo. registro del archivo} read(stock,producto); writeln('Codigo: ', producto.codigo); writeln('Descripcion: ', producto.descr); writeln('Precio unitario: ', producto.punitario); seek(stock,filesize(stock)-1); {Se posiciona en el ultimo registro} writeln(filepos(stock)); {FILEPOS devuelve la posicion actual} writeln(filesize(stock)); close(stock); end. Ejemplo 3 Una empresa nos solicita un programa que muestre en pantalla el listado de todos sus empleados con antigüedad mayor a 10 años, indicando también el nombre y apellido. Los registros del archivo de empleados contienen los siguientes campos: - Código de empleado (0..N-1, con N la cant. de empleados) - Nombre y apellido (string)

- Nro. DNI (longint)

Page 119: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 119

- Sueldo basico (real) - Retenciones (real)

Contamos además con otro archivo, cuyos registros contienen los siguientes campos: - Código del empleado (integer) - Antigüedad (integer); Se debe construir también un procedmiento que permita cargar el archivo de antigúedades. program informe; uses wincrt; type tEmpleado = record codigo: integer; nomyap: string[30]; DNI: longint; sueldo_basico: real; reten: real; end; tAntiguedad = integer; tArchEmpleados = file of tEmpleado; tArchAntiguedades = file of tAntiguedad; var empleados: tArchEmpleados; antiguedades: tArchAntiguedades; opcion: char; procedure carga(var arch: tArchAntiguedades); var antiguedad: tAntiguedad; opcion: char; i: integer; begin clrscr; rewrite(arch); i:=0; repeat begin

Page 120: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 120

writeln('Código de empleado: ', i); write('Antigüedad: '); readln(antiguedad); write('Otra antigüedad? (s/n): '); readln(opcion); writeln; write(arch,antiguedad); inc(i); end; until upcase(opcion)='N'; end; procedure informa(var arch1: tArchEmpleados; var arch2: tArchAntiguedades); var empleado: tEmpleado; antiguedad: tAntiguedad; begin clrscr; reset(arch1); reset(arch2); while not eof(arch1) do begin read(arch1,empleado); seek(arch2,empleado.codigo); read(arch2,antiguedad); if (antiguedad>10) then begin writeln('Nombre y apellido: ', empleado.nomyap); writeln('Antigüedad: ', antiguedad); write('Presione una tecla para continuar...'); readkey; writeln; end; end; end; begin {Prog. Ppal.} assign(empleados,'EMPLEADO.DAT'); assign(antiguedades,'ANTIG.DAT'); reset(empleados); {$I-} reset(antiguedades); {$I+}

Page 121: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 121

if (ioresult<>0) then rewrite(antiguedades); repeat begin clrscr; writeln('1> CARGA ANTIGÜEDADES'); writeln('2> INFORMA'); writeln('3> SALE'); write('OPCION ?: '); readln(opcion); if (opcion='1') then carga(antiguedades) else if (opcion='2') then informa(empleados,antiguedades); end; until (opcion='3'); close(empleados); close(antiguedades); end.

ARCHIVOS DE TEXTO (EJEMPLOS) Ejemplo 1 Crear un archivo de texto llamado CARTAS.TXT. Se pide un programa que le permita al usuario ir cargando línea por línea dicho archivo. program texto; var cartas: text; procedure cargalineas(var arch: text); var opcion: char; linea: string; begin repeat begin readln(linea); writeln(arch,linea); write('Otra linea? (s/n): ');

Page 122: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 122

readln(opcion); end; until upcase(opcion)='N'; end; begin {Prog. Ppal.} assign(cartas,'CARTAS.TXT'); rewrite(cartas); cargalineas(cartas); close(cartas); end. Ejemplo 2 Abrir el archivo anterior. Se pide un programa que:

- Lea secuencialmente carácter por carácter. - Imprima cada carácter por pantalla a medida que estos se van leyendo.

program texto; var carta: text; procedure imprime(var arch: text); var caracter: char; begin reset(arch); while (not eof(arch)) do begin while (not eoln(arch)) do begin read(arch,caracter); write(caracter); end; writeln; readln(arch); end; end; begin {Prog. Ppal.}

Page 123: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 123

assign(carta,'CARTAS.TXT'); reset(carta); imprime(carta); close(carta); end. Ejemplo 3 Construir un programa que abra un archivo de texto existente llamado FRASES.TXT, e imprima por pantalla todas las palabras que empiezan con N (mayúscula ó minúscula). program palabras; uses wincrt; var archtexto: text; car,anterior: char; begin {Prog. ppal.} assign(archtexto,'FRASES~1.TXT'); reset(archtexto); clrscr; while not eof(archtexto) do begin car:=’ ‘; while not eoln(archtexto) do begin anterior:=car; read(archtexto,car); if (upcase(car)='N') and (anterior=' ') then begin repeat begin write(car); read(archtexto,car); end; until (car=' '); writeln; end; end; readln(archtexto); end; readkey; close(archtexto);

Page 124: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 124

end. Ejemplo 4 Dado un archivo de texto llamado SECRETO.TXT se desea construir un programa que lo encripte en otro archivo llamado CODIGO.TXT, grabando cada carácter como el número ordinal que lo representa (Hint: valerse de la función predefinida Ord(x)). program encripta_texto; var secreto,codigo: text; procedure encripta(var arch1, arch2: text); var car: char; codigo: longint; begin reset(arch1); {En el caso de archivos de texto, Reset lo abre sólo para lectura.} while not eof(arch1) do begin while not eoln(arch1) do begin read(arch1,car); codigo:=ord(car); write(arch2,codigo); end; readln(arch1); writeln(arch2); end; end; begin {Prog. Ppal.} assign(secreto,'C:\TPW\MIOS\SECRETO.TXT'); assign(codigo,'C:\TPW\MIOS\CODIGO.TXT'); reset(secreto); rewrite(codigo); encripta(secreto,codigo); close(secreto); close(codigo); end.

Page 125: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 125

Ejemplo 5 Construir un procedimiento que cuente la cantidad de letras, de blancos y de palabras en un archivo de texto. Debe contemplar la posibilidad de que hayan dos ó más blancos seguidos. program estadistica; uses wincrt; var documento: text; procedure cuenta(var arch: text); var car,anterior: char; letras, blancos, palabras: integer; begin reset(arch); letras:=0; blancos:=0; palabras:=0; while not eof(arch) do begin anterior:=' '; while not eoln(arch) do begin read(arch,car); if (car<>' ') then inc(letras) else begin inc(blancos); if (anterior<>' ') then inc(palabras); end; anterior:=car; end; if (anterior<>' ') then inc(palabras); readln(arch); end; clrscr; writeln('Cantidad de letras: ', letras);

Page 126: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 126

writeln('Cantidad de blancos: ', blancos); writeln('Cantidad de palabras: ', palabras); readkey; end; begin {Prog. Ppal.} assign(documento,'C:\TPW\MIOS\DOCUMENT.TXT'); reset(documento); cuenta(documento); close(documento); end. Ejemplo 6 Dado que Turbo Pascal trata ciertos periféricos como archivos de texto, se desea construir un programa que abra CUENTOS.TXT (que se supone que existe), y lo imprima completo por impresora, respetando los avances de línea. program impresion; var cuentos, impresora: text; procedure imprime(var arch1,arch2: text); var car: char; begin while not eof(arch1) do begin while not eoln(arch1) do begin read(arch1,car); write(arch2,car); end; readln(arch1); writeln(arch2); end; end; begin {Prog. Ppal.} assign(cuentos,'C:\TPW\MIOS\CUENTOS.TXT');

Page 127: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 127

assign(impresora,'PRN'); reset(cuentos); rewrite(impresora); imprime(cuentos,impresora); close(cuentos); close(impresora); end.

Page 128: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO VII

Operaciones entre Archivos

Page 129: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 129

OPERACIONES ENTRE ARCHIVOS

Para poder comprender el tema tratado en este apunte, es preciso pensar a los archivos como conjuntos del álgebra. Después de todo no es una idea tan descabellada, ya que en definitiva un archivo contiene elementos, conocidos como registros. Y dados dos ó más archivos, puede suponerse que son conjuntos que contienen justamente registros, y aplicar sobre ellos las operaciones ya conocidas del álgebra de conjuntos. Estas son:

- Apareo. - Merge. - Intersección. - Unión.

Trataremos cada una de estas técnicas, pero enfatizando los temas apareo y merge. Antes de desarrollar las operaciones anteriores es preciso hacer una salvedad. Cuando hacemos referencia a conjuntos del álgebra nos vienen inmediatamente a la mente los archiconocidos Diagramas de Venn. En estos diagramas, que representamos como circunferencias, los elementos no están ordenados. Dentro de un archivo, en cambio, podemos disponer a los registros ordenadamente, lo cuál es una gran ventaja. Por consiguiente, en lo que sigue se supondrá que los archivos son binarios, y además ordenados en forma ascendente (por algún campo en particular).

Page 130: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 130

Otra aclaración interesante es el hecho de que los archivos con los que trabajamos tienen todos igual tipo de registro. Esto es muy lógico, sino intentemos hacer una intersección de dos conjuntos del álgebra, uno que contenga números y otro que contenga letras (¿cuál es el resultado...?).

APAREO La idea de aparear dos archivos es la siguiente: Se dispone de un archivo MAESTRO y otro archivo NOVEDADES. Se pretende actualizar el MAESTRO a partir de los registro de NOVEDADES, y obtener un tercer archivo, NUEVO MAESTRO. Los registros del archivo NOVEDADES son iguales a los del archivo MAESTRO, salvo que incluyen un campo indicando la operación a realizar: Alta, Baja ó Modificación. El algoritmo que se utiliza para aparear archivos consiste en tomar el primer registro de MAESTRO y el primer registro de NOVEDADES (los cuales son los más chicos, porque los archivos están ordenados ascendentemente). Acá se presentan distintas posibilidades, a saber: Si el registro más chico está en: MAESTRO: se graba este registro en el archivo NUEVO MAESTRO, y se avanza en MAESTRO una posición (recordar que Pascal avanza el puntero automáticamente). NOVEDADES: si es una alta, entonces grabo este registro en NUEVO MAESTRO. Si es baja hay que devolver un mensaje de error, ya que se está intentando borrar del archivo MAESTRO un registro que no existe en dicho archivo. Igualmente, si es una modificación deberá devolverse un error, porque no podemos modificar un registro que no existe en el archivo MAESTRO. Cualquiera sea la operación, luego se avanza una posición en el archivo NOVEDADES (lo cual Pascal realiza automáticamente cuando leímos del archivo NOVEDADES). MAESTRO Y NOVEDADES: si es una alta deberá informarse el error, ya que se está intentando dar de alta un registro que ya existe en el archivo MAESTRO. Si se trata de una baja, no se hace nada. Y si fuese una modificación, entonces se debe grabar el registro leído del archivo NOVEDADES en el archivo NUEVO MAESTRO. Después de cualquiera de estas operaciones, se avanza una posición en ambos archivos. Cuando alguno de los dos archivos (MAESTRO y NOVEDADES) queda vacío, entonces deberá copiarse el resto del archivo que aún contiene registros en NUEVO MAESTRO. Finalmente se destruyen los archivos MAESTRO y NOVEDADES.

Page 131: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 131

MERGE El merge entre archivos es similar al merge (ó mezcla) aplicado a vectores, tema que ya conocemos. La idea consiste en, a partir de dos ó más archivos ordenados ascendentemente, obtener otro archivo que incluya a todos los anteriores, y que también esté ordenados ascendentemente. Suponiendo que contamos con dos archivos que llamaremos ARCHIVO1 y ARCHIVO2, los cuales, como ya dijimos, están ordenados de menor a mayor, y un archivo de salida llamado MEZCLA. El algoritmo es el siguiente: Tomamos el primer registro de ambos archivos (que son los más chicos). Si éste se encuentra en: ARCHIVO1: se graba este registro en MEZCLA, y se avanza una posición (una vez más, esto en Pascal se realiza automáticamente). ARCHIVO2: se graba este registro en MEZCLA, y se avanza una posición. ARCHIVO1 y ARCHIVO2: se graban ambos registros en el archivo MEZCLA. Cuando alguno de los archivos ARCHIVO1 ó ARCHIVO2 se terminen, entonces se copia el resto del archivo que aún contenga registros en MEZCLA. Finalmente MEZCLA contiene todos los registros de ARCHIVO1 y de ARCHIVO2, ordenados ascendentemente.

Page 132: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 132

INTERSECCIÓN

Como con conjuntos, la intersección de archivos consiste en, a partir de dos ó más archivos ordenados ascendentemente, obtener otro archivo (ordenado) que contenga sólo aquellos registros que son comunes a todos los archivos intersecados. La intersección, al igual que la unión entre archivos, es una operación poco usada respecto al apareo y al merge, con lo cual nos conformaremos con dar la definición.

Page 133: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 133

UNIÓN

A partir de dos ó más archivos, se pretende obtener uno nuevo que contenga todos los registros de los archivos anteriores (igual que con el álgebra de conjuntos). Si prestamos un poco de atención, podría parecernos que la unión es igual al merge de archivos. Sin embargo, la diferencia radica en que en la unión no se admiten registros repetidos en el archivo de salida, cosa que si está permitida en el merge.

Page 134: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 134

OPERACIONES ENTRE ARCHIVOS (EJEMPLOS) Ejemplo 1 (apareo) En un depósito de mercaderías se empléa un archivo llamado MERC.DAT para almacenar información referente a cada producto. Al final del día se debe actualizar dicho archivo para dejar constancia de las operaciones realizadas en cada jornada diaria. Esta información se halla en un archivo llamado NOVE.DAT. Cada registro del archivo MERC.DAT contiene los siguientes campos:

- Código de producto (integer). - Descripción (string). - Unidades en existencia (integer);

Para los registros del archivo NOVE.DAT los campos son:

- Código de producto (integer). - Cantidad vendida ó comprada (negativo significa venta, y positivo

significa compra). Se pide un programa que actualize el archivo MERC.DAT a partir del archivo NOVE.DAT, y devuelva el resultado en el archivo NUEVO.DAT. Todos los archivos están ordenados ascendentemente por Código de producto. program apareo_archivos; uses wincrt; type tReg1=record cod,unidades: integer; descr: string; end; tReg2=record cod: integer; cantidad: integer; {cantidad>0 -> COMPRA ; cantidad<0 -> VENTA} end; tArch1=file of tReg1; tArch2=file of tReg2; var maestro,nuevo: tArch1; novedades: tArch2;

Page 135: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 135

procedure apareo(var arch1: tArch1; var arch2: tArch2; var arch3: tArch1); var reg1: tReg1; reg2: tReg2; encontrado: boolean; begin while not eof(arch2) do begin read(arch2,reg2); encontrado:=false; while (not eof(arch1) and not encontrado) do begin read(arch1,reg1); if (reg1.cod=reg2.cod) then begin reg1.unidades:=(reg1.unidades)+(reg2.cantidad); encontrado:=true end; write(arch3,reg1) end end; while not eof(arch1) do begin read(arch1,reg1); write(arch3,reg1) end end; begin {Prog. ppal.} assign(maestro,'MERC.DAT'); assign(novedades,'NOVE.DAT'); assign(nuevo,'NUEVO.DAT'); reset(maestro); reset(novedades); rewrite(nuevo); apareo(maestro,novedades,nuevo); imprime(nuevo); close(maestro); close(novedades); close(nuevo) end.

Page 136: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 136

Ejemplo 2 (intersección) Se tiene dos archivos que contienen números enteros ordenados ascendentemente, llamados NUMEROS1.DAT y NUMEROS2.DAT. Se pide un programa que realice la intersección de ambos, y el resultado lo almacene en el archivo INTER.DAT. program interseccion_archivos; uses wincrt; type tArch=file of integer; var numeros1,numeros2,inter: tArch; procedure interseccion(var arch1, arch2, arch3: tArch); var num1,num2: integer; lee1,lee2,encontrado: boolean; begin reset(arch1); reset(arch2); lee1:=true; lee2:=true; while (not eof(arch1) and not eof(arch2)) do begin if (lee1) then read(arch1,num1); if (lee2) then read(arch2,num2); if (num1=num2) then begin write(arch3,num1); lee1:=true; lee2:=true end else if (num1<num2) then begin lee1:=true; lee2:=false end else begin lee1:=false;

Page 137: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 137

lee2:=true end end; encontrado:=false; if (eof(arch1) and not lee1) then while (not eof(arch2) and not encontrado) do begin read(arch2,num2); if (num1=num2) then begin write(arch3,num1); encontrado:=true end else if (num1<num2) then encontrado:=true end; encontrado:=false; if (eof(arch2) and not lee2) then while (not eof(arch1) and not encontrado) do begin read(arch1,num1); if (num1=num2) then begin write(arch3,num1); encontrado:=true end else if (num2<num1) then encontrado:=true end; end; begin {Prog. ppal.} assign(numeros1,'NUMEROS1.DAT'); assign(numeros2,'NUMEROS2.DAT'); assign(inter,'INTER.DAT'); rewrite(numeros1); rewrite(numeros2); rewrite(inter); carga(numeros1); carga(numeros2); interseccion(numeros1,numeros2,inter); imprime(inter);

Page 138: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 138

close(numeros1); close(numeros2); close(inter) end. Ejemplo 3 (apareo) El cajero automático de una entidad bancaria cuenta en su sistema con un dispositivo de almacenamiento en donde se registran todas las operaciones de los clientes que a él acuden. Estas quedan guardadas en un archivo binario llamado OPERAC.DAT. Al finalizar el día, la sucursal lee el archivo del cajero automático, y actualiza su base de datos, para dejar constancia de todas las operaciones realizadas por sus clientes. Se pide un programa que realice el aparéo entre el archivo del cajero y el archivo del banco, llamado CLIENTES.DAT, y el resultado lo almacene en un archivo llamado AUXILIAR.DAT. Luego debe eliminarse CLIENTES.DAT, y renombrar AUXILIAR.DAT para que pase a llamarse como el archivo eliminado. Los registros del archivo OPERAC.DAT tienen los siguientes campos:

- Código de cliente (integer). - Fecha (string[10]). - Hora (string[10]). - Operación (D: depósito; E: extracción; C: consulta); - Monto en pesos, para el caso de depósito ó extracción (real).

Los registros del archivo CLIENTES.DAT tienen los siguientes campos:

- Código de cliente (integer). - Nombre y apellido (string). - Monto disponible (real).

Ambos archivos están ordenados por Código de cliente, y se supone que cada cliente realizó una única operación en un día. program apareo_archivos; uses wincrt; type tCliente=record cod: integer; nomyap: string; monto: real; end; tOperac=record

Page 139: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 139

cod: integer; fecha, hora: string[10]; operac: char; monto: real; end; tArchClien=file of tCliente; tArchCajero=file of tOperac; var clientes,auxiliar: tArchClien; cajero: tArchCajero;

procedure apareo(var arch1: tArchClien; var arch2: tArchCajero; var arch3: tArchClien); var reg1: tCliente; reg2: tOperac; encontrado: boolean; begin reset(arch1); reset(arch2); while not eof(arch2) do begin read(arch2,reg2); encontrado:=false; while (not eof(arch1) and not encontrado) do begin read(arch1,reg1); if (reg1.cod=reg2.cod) then begin if (upcase(reg2.operac)='D') then {ES UN DEPOSITO} reg1.monto:=reg1.monto+reg2.monto else if (upcase(reg2.operac)='E') then {ES UNA EXTRACCION} reg1.monto:=reg1.monto-reg2.monto; encontrado:=true end; write(arch3,reg1) end end; while not eof(arch1) do begin read(arch1,reg1);

Page 140: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 140

write(arch3,reg1) end end; begin {Prog. ppal.} assign(clientes,'CLIENTES.DAT'); assign(cajero,'OPERAC.DAT'); assign(auxiliar,'AUXILIAR.DAT'); reset(clientes); reset(cajero); rewrite(auxiliar); apareo(clientes,cajero,auxiliar); close(clientes); erase(clientes); {ELIMINA EL ARCHIVO FISICO CLIENTES.DAT} close(cajero); close(auxiliar); rename(auxiliar,'CLIENTES.DAT') end.

Page 141: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO VIII

Claves

Page 142: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 142

CLAVES

Cuando trabajamos con conjuntos de datos (por ejemplo un archivo ó un vector en memoria) generalmente necesitamos ubicar un dato en particular, y para ello utilizamos “algo” que identifique al elemento buscado. Por ejemplo, en un vector podría ir a la posición [10] a leer la componente que me interesa. Para ello utilizaría la sentencia VECTOR[10], y obtendría una única posición del vector. En un archivo directo, haciendo SEEK(ARCH,5) me posiciono sobre un registro en particular (pero sólo en un registro). Entonces, la primer idea que podemos extraer es que: Para acceder a un dato en particular necesito información que represente unívocamente al elemento buscado. Lo de unívocamente viene del hecho de que, dado un dato debería poder conocer su “algo que lo identifica”, y dado el “algo que lo identifica” debería poder conocer el dato. La relación es estrictamente uno a uno:

DATO ⇔ IDENTIFICACIÓN DEL DATO Ese “algo” que identifica a un dato es a lo que se denomina clave. Por lo tanto, podemos dar una definición sencilla y básica diciendo que: Una clave es la parte de un dato que lo identifica unívocamente (sin ambigüedad) en un conjunto de datos, de manera tal que para referenciarlo sea suficiente dar su clave.

DATO ⇔ CLAVE Una clave es para un dato lo que el D.N.I. es para las personas. En el ámbito de nuestra Facultad, una buena clave es el número de padrón de cada alumno. Esto significa que si doy un padrón determinado, pueden decirme a quién corresponde, y no hay posibilidad de que dicho padrón corresponda a dos alumnos simultáneamente. Las claves no necesariamente son datos atómicos. Es decir que, una clave, puede estar formada por varios datos de un elemento en particular, siempre con la condición de que lo identifiquen unívocamente. En este caso hablamos de claves compuestas. Supongamos que las personas no tuvieran D.N.I., y quisiera hacer referencia a un tal Juan Pérez, que vive en Av. Córdoba 1250, en el 4to. piso, departamento “B”, y tiene 24 años de edad. Primero doy el nombre, o sea Juan Pérez. Pero pueden haber muchos Juan Pérez en el mundo (de hecho

Page 143: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 143

los hay), con lo cual ese dato no me identifica a la persona a la que hago referencia. Ahora digo Juan Pérez, el que vive en Av. Córdoba al 1250. El espacio de búsqueda se acota muchísimo (de todo el mundo a la altura 1250 de Av. Córdoba), pero en ese mismo edificio podría haber más de un Juan Pérez. Entonces digo Juan Pérez, que vive en Av. Córdoba 1250, en el departamento 4to. “B”. La situación mejora mucho más, y la búsqueda se localiza dentro de un departamento. La cuestión es que aún podría haber más de un Juan Pérez en ese departamento (por ejemplo, el padre y el hijo). Entonces, finalmente digo Juan Pérez, que vive en Av. Córdoba 1250, departamento 4to. “B” y tiene 24 años de edad. Ahora si queda unívocamente definida la persona que estoy buscando. La clave de búsqueda en este caso tendría la forma: NOMBRE Y APELLIDO + DIRECCIÓN + DEPARTAMENTO + EDAD En informática, el uso de claves cobra particular importancia cuando empleamos tablas. Es decir, vectores cuyos componentes son registros (datos estructurados). Una posibilidad podría ser el listado de alumnos que tiene la Facultad de Ingeniería en su base de datos. La forma sería la siguiente:

Padrón Nombre y Apellido D.N.I. Dirección Edad . . . . . . . . . . . . . . .

76.054 Rinaldi, Jorge 25.982.901 Colombres 1243 4° “D” 25 76.055 Frías, Andrea 26.562.777 Av. De los Incas 49 6°

“H” 24

76.056 Rodríguez, Pablo 24.872.662 Carlos Calvo 345 1° “A” 26 76.057 Rinaldi, María 27.021.392 Colombres 1243 4° “D” 22

. . . . . . . . . . . . . . . Del conjunto de datos anterior tendríamos que elegir uno ó más campos que identifiquen de manera única a cada registro de la tabla (es decir, a cada fila). En primera instancia podríamos optar por el número de padrón, ya que todo alumno tiene número de padrón, y además cada número de padrón corresponde a un único alumno. Otra posibilidad interesante sería elegir el D.N.I. como clave de nuestro conjunto de datos. ¿Estaría bien elegir como clave el nombre y apellido? ¡Absolutamente no! Por la sencilla razón de que pueden haber dos alumnos (ó más) con igual nombre y apellido. Este mismo argumento se aplica para la dirección y la edad, que tampoco pueden elegirse como claves. ¿Pero que sucede si elijo los campos nombre y apellido, dirección y edad en conjunto? Bueno, la cosa cambia. Si suponemos que en un domicilio en particular no viven dos personas con igual nombre y apellido e igual edad, entonces podría ser una posible clave. ¿Se le ocurre alguna otra clave para ese conjunto de datos? De todas las claves que propusimos, particularmente prefiero la del número de padrón. ¿Por qué? Bueno, primero porque es un dato numérico, y a la hora de elegir claves es una buena regla optar por campos que sean números. En ese caso también podría haber elegido el DNI, pero el padrón es más corto, y por consiguiente es más fácil de manejar. En segundo lugar, se trata de un dato atómico, a diferencia de la clave nombre y apellido, dirección y

Page 144: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 144

edad. Y por último, puede verse en la tabla que el campo número de padrón está ordenado en forma creciente, con lo cual, dada la clave, puedo hallar rápidamente el registro aplicando una simple búsqueda binaria. No hay técnicas generales a la hora de buscar una clave, pero sí existen herramientas que permiten decidir sobre un conjunto de datos en particular. Es decir que, para establecer la clave, deben analizarse los datos con los que estoy trabajando. La clave depende del conjunto de datos. Existen diferentes tipos de claves. Nombraremos los principales: Superclave: conjunto de campos que identifican unívocamente a cada registro de un conjunto de datos. Clave candidata (candidate key): superclave tal que al quitarle alguno de sus campos deja de ser clave. Es una clave mínima. Clave primaria (primary key): es aquella clave candidata que hemos seleccionado para representar a los registros de un conjunto de datos. Dicha selección debe hacerse bajo algún criterio. Clave secundaria (secundary key): uno ó más campos de datos que no necesariamente representan unívocamente a cada registro, pero que permite acceder a un conjunto de ellos que matchean dicha clave secundaria. Por ejemplo, todos los alumnos de la Facultad de Ingeniería que viven en la calle Colombres. Una regla interesante:

Elegir como clave primaria aquella clave candidata con menor número de campos. Este apunte constituye una introducción muy elemental al manejo de claves. El tema es por demás de importante, ya que de la elección de una buena clave depende el funcionamiento futuro de nuestra base de datos. Una buena costumbre sería ponerlo en práctica con nuestros simples y pequeños vectores y archivos.

Page 145: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 145

CLAVES (EJEMPLOS) Ejemplo 1 En un sanatorio se cuenta con un archivo en donde se almacenan las historias clínicas de todos los pacientes. Este archivo se llama HISTO.DAT, y los campos de los registros son:

- Código de historia clínica (integer). - Nombre y apellido del paciente (string). - Edad del paciente (integer). - Rubeola (boolean). - Hepatitis (boolean). - Pulmonía (boolean). - Nombre y apellido del médico de cabecera (string);

Se desea construir un programa que a partir de un Código de historia clínica busque en el archivo el paciente correspondiente, e imprima los datos por pantalla. El archivo HISTO.DAT no está ordenado por ningún campo, con lo cual deberá recorrerse secuencialmente. Esto no significa un costo demasiado alto si suponemos que el archivo HISTO.DAT tiene pocas historias clínicas guardadas. Además, el campo Código de historia clínica es la clave del archivo. program clinica; uses wincrt; type tPaciente=record cod: integer; nomyap,medico: string; edad: byte; {0..255} rubeola,hepatitis,pulmonia: boolean; end; tArch=file of tPaciente; var historias: tArch; codigo: integer; procedure consulta(var arch: tArch; cod: integer); var reg: tPaciente; encontrado: boolean;

Page 146: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 146

begin reset(arch); encontrado:=false; while (not eof(arch) and not encontrado) do begin read(arch,reg); if (reg.cod=cod) then encontrado:=true end; if (encontrado) then with reg do begin writeln('Código de historia: ', cod); writeln('Nombre y apellido del paciente: ', nomyap); writeln('Edad del paciente: ', edad); write('Tuvo rubeola: '); if (rubeola) then writeln('SI') else writeln('NO'); write('Tuvo hepatitis: '); if (hepatitis) then writeln('SI') else writeln('NO'); write('Tuvo pulmonia: '); if (pulmonia) then writeln('SI') else writeln('NO'); writeln('Nombre y apellido del médico de cabecera: ', medico); end else writeln('NO SE HA ENCONTRADO LA HISTORIA CLINICA'); writeln; write('Presione una tecla...'); readkey end; begin {Prog. ppal.} CheckBreak:=false; {DESHABILITA EL USO DE CTRL-BREAK} assign(historias,'HISTO.DAT'); reset(historias); repeat clrscr; write('Ingrese el código de historia clínica (0=FIN): ');

Page 147: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 147

readln(codigo); writeln;

if (codigo>0) then consulta(historias,codigo)

until (codigo=0); close(historias) end. Ejemplo 2 Se tienen dos archivos binarios, uno secuencial y otro directo, con información sobre los productos que se venden en un supermercado. El archivo secuencial es muy pequeño, tal que entra completo en memoria. Cada registro de este archivo tiene los siguientes campos:

- Descripción (string). - Disponibilidad (integer); - Código de proveedor (integer).

El archivo directo, es muy grande, y cada registro contiene los campos:

- Razón social del proveedor (string); - Dirección (string); - Localidad (string); - Teléfono(string);

En este archivo, el índice que representa a cada registro del mismo cumple el papel de Código de proveedor. Se pide un programa que:

- Cargue en un vector en memoria el archivo secuencial. - Imprima por pantalla todos los productos tales que la disponibilidad sea

menor que 10 unidades, con los datos de su proveedor. El archivo secuencial está ordenado por Descripción del producto. program supermercado; uses wincrt; const MAX=10; type tProducto=record

Page 148: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 148

descr: string; disp, cod_prov: integer; end; tProveedor=record razon, direccion, local, tel: string; end; tVector=array[1..MAX] of tProducto; tArchProd=file of tProducto; tArchProv=file of tProveedor; var prod: tArchProd; prov: tArchProv; tabla: tVector; n: integer; procedure binaria(tabla: tVector; descr: string; n: integer; var pos: integer); var inf,sup,medio: integer; encontrado: boolean; begin inf:=1; sup:=n; encontrado:=false; repeat begin medio:=(inf+sup) div 2; if (descr=tabla[medio].descr) then encontrado:=true else if (descr<tabla[medio].descr) then sup:=medio-1 else inf:=medio+1 end until (encontrado) or (inf>sup); if (encontrado) then pos:=medio else pos:=0 end;

Page 149: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 149

procedure carga(var arch1: tArchProd; var arch2: tArchProv); var producto: tProducto; proveedor: tProveedor; opcion: char; begin rewrite(arch1); rewrite(arch2); clrscr; repeat begin write('Descripcion: '); readln(producto.descr); write('Disponibilidad: '); readln(producto.disp); write('Cod. proveedor: '); readln(producto.cod_prov); write(arch1,producto); write('¿Otro producto? (s/n): '); readln(opcion); writeln end until upcase(opcion)='N'; repeat begin write('Razon social: '); readln(proveedor.razon); write('Direccion: '); readln(proveedor.direccion); write('Localidad: '); readln(proveedor.local); write('Telefono: '); readln(proveedor.tel); write(arch2,proveedor); write('¿Otro proveedor? (s/n): '); readln(opcion); writeln end until upcase(opcion)='N' end; procedure carga_memoria(var arch: tArchProd; var tabla: tVector; var n: integer); var producto: tProducto;

Page 150: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 150

begin n:=0; reset(arch); while not eof(arch) do begin read(arch,producto); inc(n); tabla[n]:=producto end; end; procedure imprime(tabla: tVector; var arch: tArchProv; n: integer); var posicion: integer; proveedor: tProveedor; descr: string; opcion: char; begin repeat begin clrscr; write('Ingrese la descripcion: '); readln(descr); writeln; binaria(tabla,descr,n,posicion); if (tabla[posicion].disp<10) then begin seek(arch,tabla[posicion].cod_prov); read(arch,proveedor); with tabla[posicion] do begin writeln('Descripcion: ', descr); writeln('Disponibilidad: ', disp); writeln('Cod. proveedor: ', cod_prov) end; with proveedor do begin writeln('Razon social: ', razon); writeln('Direccion: ', direccion); writeln('Local: ', local); writeln('Telefono: ', tel) end end else

Page 151: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 151

writeln('Disponibilidad suficiente.'); writeln; write('¿Otro producto? (s/n): '); readln(opcion) end until upcase(opcion)='N' end; begin {Prog. ppal.} assign(prod,'C:\TPW\MIOS\PROD.DAT'); assign(prov,'C:\TPW\MIOS\PROV.DAT'); reset(prod); reset(prov); carga(prod,prov); carga_memoria(prod,tabla,n); imprime(tabla,prov,n); close(prod); close(prov); end.

Page 152: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO IX

Índices

Page 153: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 153

INDICES

Siempre que debamos consultar un libro en busca de algún tema en particular, el sentido común nos lleva a utilizar el índice del mismo que, en general, forma parte de las primeras ó de las últimas páginas, de manera tal que el lector pueda acceder fácilmente a él. Si es un índice alfabético tendremos un conjunto de ‘palabras clave’ ordenadas alfabéticamente, y localizando aquella de nuestro interés obtenemos un número de página, indicando en qué parte del libro encontraremos el tema buscado. Esto suele representar una ventaja significativa si tenemos en cuenta que un libro común puede contener muchas hojas. Estos sencillos pasos nos facilitan la tarea, y evitan que debamos recorrer hoja tras hoja para encontrar aquél capítulo o párrafo que estamos buscando. Pero... ¿alguien se ha puesto a pensar qué pasaría si El Capital de Karl Marx no tuviese índice? Mientras escribo esto, se me cruza una idea por la cabeza, la cuál no puedo dejar de comentar. Pienso que podríamos evitar el uso de índices en los libros, simplemente vendiendo libros cuyo contenido esté ordenado alfabéticamente (al mejor estilo de un diccionario ó guía telefónica). Si, claro, ya sé que no entenderíamos nada, pero nos evitamos el uso de índices. Como sé que mi idea no les satisface, vamos a establecer una primer regla:

Regla Nro. 1 Un libro que se precie de tal debe poseer un índice.

En el siguiente esquema pueden apreciarse cuáles son los pasos a seguir para poder consultar un determinado tema en un libro:

TEMA A BUSCAR

Voy al índice

OBTENGO EL N° DE PÁGINA

Voy al libro

ACCEDO A LA PÁGINA

DETERMINADA De esta forma establezco la siguiente regla:

Page 154: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 154

Regla Nro. 2

El índice de un libro permite acceder con facilidad (en forma directa) a una cierta página del mismo, sin necesidad de recorrerlo hoja por hoja.

Pero no solamente los libros poseen índices. Supongamos que voy a una biblioteca a pedir el libro El Capital. La bibliotecaria, muy amablemente, accedería a una carpeta en donde tiene registradas todas las obras literarias, ordenadas por nombre, con lo cual rápidamente encontraría el título El Capital, y obtendría el número de estante en donde se encuentra dicha obra. Vuelvo al día siguiente, pero ahora quiero alguna obra de Karl Marx. Como en esa biblioteca la gente es muy eficiente, disponen de otra carpeta en donde figuran todos los libros, pero ordenados por nombre y apellido del autor. Nuevamente, encontraría rápidamente la ó las obras de Karl Marx, y, junto a ellas, los números de estante correspondientes. Digamos que esas carpetas en donde la biblioteca registra la información y ubicación de cada uno de sus libros juegan el papel de índices, y por consiguiente surge la siguiente regla:

Regla Nro. 3 Los índices no sólo sirven para acceder a una determinada ubicación en forma directa, sino que también permiten imponerle un orden a ‘algo’, sin necesidad de ordenar ese

‘algo’. Claro, imaginemos qué pasaría si la biblioteca a la cual concurro a menudo tuviese que reordenar todos los tomos cada vez que alguien busca una obra en particular... Bueno, hasta ahora muy linda la introducción, pero ¿dónde están los algoritmos? ¿cuándo aparece Pascal? Sucede que el tema índices no es propio de la informática, sino que esta disciplina ‘toma prestado’ el concepto para aplicarlo en determinadas circunstancias, en esos momentos en los que debemos lidiar con archivos de datos (sobre todo aquellos que contienen mucha información). Implementación de índices en el manejo de archivos de datos Una aproximación al tema de archivos nos permite definirlos como conjuntos de datos, con una cierta estructura, y cuyo almacenamiento se lleva a cabo en dispositivos de memoria auxiliar ó secundaria (discos rígidos, disquetes, CD-ROMs, etc.). Esa ‘cierta estructura’ puede constituirse a partir del uso de registros (aunque es cierto que en muchas oportunidades los archivos se organizan como ‘tiras’ de bytes). Lo cierto es que ellos pueden crecer en gran medida, y dificultarnos un poco las tareas. En primer lugar, si un archivo se expande, también crece el número de registros a consultar en el caso de una búsqueda. Y, en segundo lugar, la memoria auxiliar ó secundaria no nos provee una performance aceptable en lo que a entrada / salida de datos se refiere. Busquemos, pues,

Page 155: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 155

técnicas más apropiadas, y que nos permitan hacer uso de la memoria principal a la hora de efectuar búsquedas en grandes archivos (y en los que no son tan grandes también). La pregunta que surge es la siguiente: ¿Por qué las búsquedas en disco son ineficientes? ó ¿Por qué es mejor utilizar la memoria principal? Si después de todo, un acceso a disco tarda “menos que un segundo”... Es verdad que el hecho de ir a disco en busca de un dato insume un tiempo irrisorio (en particular, un acceso a disco implica un tiempo del orden de los los milisegundos, la milésima parte de un segundo!!!). Pero también es verdad que acceder a memoria principal es bastante más rápido (algo así como 70 nanosegundos, es decir, una milmillonésima parte de un segundo!!!). Pero ¿qué significa “bastante más rápido”? Veamos la siguiente relación: Si una operación en memoria principal insumiese 1 minuto, entonces esa misma operación en un disco rígido de los más veloces tardaría aproximadamente 2 años. Y, sin exagerar, si una determinada operación tardara en memoria principal un tiempo de 1 segundo, en un moderno disco rígido insimuría más de 10 días. Sorpredente ¿no?... Ahora vemos porqué es tan importante poder trabajar en memoria principal, accediendo lo menos posible a las unidades de almacenamiento externo. De este análisis surge la necesidad de implementar alguna estructura especial para buscar un dato en disco, la cual haga uso de la memoria principal. Esa estructura tan ansiada es lo que se conoce como índice. Por lo visto hasta ahora, un índice contiene un conjunto de información, que podemos clasificar de la siguiente forma:

§ Claves. § Referencias (al archivo).

Claves: en un índice, el conjunto de claves corresponde al ó los campos por los cuales se realiza la búsqueda. Para el ejemplo de la biblioteca, el conjunto de claves para dicho índice podría ser el título de la obra, nombre y apellido del autor, etc. Referencias: el conjunto de referencias está constituido por las posiciones (offsets) de los registros en el archivo de datos, para una clave dada. Volviendo al ejemplo de la biblioteca, nuestras referencias serían los números de estante en donde se halla el libro buscado. Todo esto se resume en el siguiente esquema:

Page 156: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 156

En el ámbito de los archivos, toda esta estructura se traduce de la siguiente manera: § La biblioteca con todos sus estantes corresponde al archivo en si mismo, el cual se

almacena en unidades secundarias (discos, CD-ROMs, etc.). § Las carpetas (de las cuales dispone la biblioteca) con el detalle de todos los libros

ordenados por diferentes claves corresponden a las estructuras de índices. En el mundo del procesamiento de archivos, los índices se trabajan en memoria principal, aunque en la realidad esto es un tanto utópico, y se trata al índice parte en memoria principal y parte en memoria secundaria, técnica conocida como “paginación”. Como en el caso de los libros, en un sistema informático pueden convivir tantos índices al mismo tiempo como sea necesario (cada uno de ellos por diferentes claves, de manera tal de poder acceder al mismo archivo por distintos caminos). En muchas ocasiones el número de índices abiertos simultáneamente está restringido por la finitud de los recursos de los cuales se dispone (memoria, espacio en disco, etc.).

Esta analogía se puede expresar gráficamente de la siguiente forma:

Page 157: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 157

Como puede apreciarse en el esquema anterior, el archivo en disco contiene una cierta cantidad de datos, que no necesariamente deben estar en los índices. Es por ello que en general los índices ocupan mucha menos cantidad de memoria que el archivo de datos al cual indexan. Índices primarios y secundarios Existe una gran clasificación dentro de la teoría de índices: índices primarios e índices secundarios, que tiene que ver con los tipos de claves empleadas para indexar un conjunto de datos (recomendamos que antes de seguir con este tema se lea el capítulo sobre claves). Índices primarios: este tipo de índices tiene como característica que la clave por la cual se indexa es primaria, es decir unívoca (sin posibilidad de que se repita). Por ejemplo, si quisiéramos indexar la base de datos de los alumnos de la Facultad de Ingeniería de la UBA, una posibilidad sería hacerlo por número de padrón. Dicho campo es clave primaria, y por lo tanto el índice construido a partir de ella se denomina índice primario. Debe quedar claro que en un índice primario cada entrada tiene una única referencia al archivo de datos, porque justamente no pueden existir dos ó más registros con igual clave. Índices secundarios: en este caso se indexa por una clave denominada secundaria (es decir que puede repetirse dentro del conjunto de datos). Una clave secundaria no es unívoca, y por lo tanto es posible que en el índice hayan varias referencias para una misma clave. Supongamos ahora que queremos indexar la base de datos de la Facultad de Ingeniería, pero por nombre y apellido del alumno. Ya que pueden haber varios alumnos con igual nombre y apellido, una entrada del índice puede “apuntar” a más de un registro de la base de datos.

Page 158: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 158

Para un conjunto de datos en particular (base de datos), solamente puede existir un índice primario, mientras que pueden convivir varios índices secundarios simultáneamente. Debe quedar claro que la clave primaria es única, por lo tanto el índice primario también lo es. Resumen Un índice sirve para: § Acceder a un conjunto de datos por diferentes caminos (claves). § Imponerle un orden a un conjunto de datos, sin necesidad de reordenar el archivo

para ello. § De las dos características anteriores se deriva que un índice permite un acceso

rápido a una base de datos. Todo índice está constituido por: § Claves. § Referencias u offsets al archivo.

Los índices pueden clasificarse en: § Primarios (indexan por clave primaria, y es único). § Secundarios (indexan por clave secundaria, y puede existir más de uno).

Se desea que los índices sean trabajados en memoria principal, para obtener una mayor velocidad de acceso, aunque esto no es posible en muchos casos. Ante esta situación, parte del índice se carga y se accede en memoria principal, y el resto se lo conserva en disco (hasta que deba ser utilizado). Esta técnica se conoce como paginación.

Page 159: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 159

INDICES (EJEMPLOS) Ejemplo 1 Construir un programa en Pascal que permita cargar un archivo, cuyos registros contienen los siguientes campos:

§ Nombre y apellido (cadena de caracteres). § Edad (entero). § Nro. de DNI (entero largo).

Luego, un procedimiento deberá indexar dicho archivo, construyendo un índice en memoria (el cual deberá ser ordenado). Finalmente debe solicitar una clave al usuario para imprimir el correspondiente registro por pantalla (la clave por la cual se indexa es el número de DNI). program indices; uses crt; const MAX_INDICE = 20; type t_reg = record nomyap: string[20]; edad: integer; dni: longint; end; t_arch = file of t_reg; t_reg_indice = record dni, posicion: longint; end; t_indice = array[1..MAX_INDICE] of t_reg_indice; procedure carga(var arch: t_arch); var registro: t_reg; opcion: char;

Page 160: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 160

begin reset(arch); clrscr; repeat write('Nombre y apellido: '); readln(registro.nomyap); write('Edad: '); readln(registro.edad); write('DNI Nro.: '); readln(registro.dni); write(arch,registro); writeln; write('Otro registro (s/n)? '); readln(opcion); until upcase(opcion) = 'N'; end; procedure ordena(var indice: t_indice); var i, recorrido: integer; procedure intercambiar(var reg1, reg2: t_reg_indice); var aux: t_reg_indice; begin aux:=reg1; reg1:=reg2; reg2:=aux; end; begin for recorrido:=1 to MAX_INDICE-1 do for i:=1 to MAX_INDICE-recorrido do if (indice[i].dni>indice[i+1].dni) then intercambiar(indice[i],indice[i+1]); end; procedure indexa(var arch: t_arch; var indice: t_indice); var registro: t_reg;

Page 161: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 161

reg_indice: t_reg_indice; pos: longint; begin reset(arch); pos := 0; while not eof(arch) do begin read(arch,registro); reg_indice.dni := registro.dni; reg_indice.posicion := pos; indice[pos+1] := reg_indice; inc(pos); end; ordena(indice); end; function busqueda_binaria(indice: t_indice; dni: longint): integer; var i, inf, sup, medio: integer; begin inf:=1; sup:=MAX_INDICE; i:=0; repeat medio:=(inf+sup) div 2; if (dni=indice[medio].dni) then i:=medio else if (dni<indice[medio].dni) then sup:=medio-1 else inf:=medio+1; until (i<>0) or (inf>sup); if (inf>sup) then busqueda_binaria:=0 else busqueda_binaria:=i; end;

Page 162: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 162

procedure imprime(var arch: t_arch; indice: t_indice; dni: longint); var pos_indice: integer; pos_archivo: longint; registro: t_reg; begin pos_indice := busqueda_binaria(indice,dni); pos_archivo := indice[pos_indice].posicion; seek(arch,pos_archivo); read(arch,registro); writeln('Nombre y apellido: ', registro.nomyap); writeln('Edad: ', registro.edad); writeln('DNI Nro.: ', registro.dni); readkey; end; var archivo: t_arch; indice: t_indice; dni: longint; begin {Prog. Ppal.} assign(archivo,'DATOS.DAT'); {$I-} reset(archivo); if ( ioresult <> 0 ) then rewrite(archivo); {$I+} carga(archivo); indexa(archivo,indice); write('Ingrese un DNI: '); readln(dni); imprime(archivo,indice,dni); close(archivo); end.

Page 163: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO X

Recursividad

Page 164: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 164

RECURSIVIDAD

La recursividad es un concepto muy importante en programación, y hace referencia a la capacidad que tienen los subprogramas (sean funciones ó procedimientos) de autoinvocarse. Esto es, que un subprograma se llame a si mismo (mediante su propio nombre), y desde dentro de su propio código. La singular importancia de ésta técnica tiene que ver con que en muchas ocasiones debemos programar algo que es de naturaleza recursiva. Un ejemplo claro sería si quisiéramos hacer un algoritmo que calcule el factorial de un número. Matemáticamente, la expresión que permite hallar el factorial de N viene dada por: 1 si N = 0 Factorial(N) = N ! = N * (N-1)! si N > 0 Esto significa que, si N=0, el factorial de N (o sea N!) es 1, pero si N>0 el factorial de N es N*factorial(N-1). Es decir que, para calcular el factorial de un número debemos ir calculando los factoriales de los números que lo anteceden. Por ejemplo, el factorial de 4 (que se expresa 4!) está dado por: 4! = 4*3! = 4*3*2! = 4*3*2*1! = 4*3*2*1*0! = 4*3*2*1*1 = 24 Para programar este algoritmo construimos una función que reciba como parámetro el número N, y devuelva el resultado de calcular el factorial de N. El encabezado de dicha función tendría la siguiente forma:

function factorial(N: integer): longint; Cuando se le pasa un valor N por parámetro a la función anterior, ésta deberá analizar si N es igual ó mayor que cero. En el primer caso, la función devuelve 1, y en el segundo caso la función se invoca a si misma (desde dentro de su propio código), de la siguiente manera: . . . fact := factorial(N-1); . . . La sentencia anterior expresa una llamada recursiva. Cuando se llama a factorial(N-1) sucede lo mismo que cuando se llamó a factorial(N) la primera vez, salvo que el número pasado por parámetro ahora es N-1, pero el código que se ejecuta es siempre el mismo, ó sea el correspondiente a la función factorial( ).

Page 165: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 165

La recursividad puede producirse cuantas veces sea necesario (“anidamiento de llamadas recursivas”). Es decir que un subprograma podrá autoinvocarse tantas veces quiera. Ver sino que sucedería si quisiéramos calcular el factorial de 4. La primera vez invocamos la función pasándole el número 4 como parámetro. Dentro de la función hay una autoinvocación, pero ahora el parámetro es N-1, o sea 3. Luego habrá una nueva autoinvoación con el parámetro 2, y así sucesivamente hasta que el parámetro pasado sea N=0. La pregunta que viene a la mente después de hacer este breve análisis es la siguiente: si las llamadas recursivas de un subprograma se van anidando ¿cuándo se corta la ejecución de esto que parecería ser un ciclo sin fin? Indudablemente necesitamos algo que en un momento determinado “corte” ese ciclo de autoinvocaciones. Ese “algo” que estamos buscando es lo que se denomina una condición de corte. Es decir, una sentencia que en un momento opte por no autoinvocar al subprograma. En el caso del cálculo del factorial, la condición de corte está dada por: Factorial(N) = 1 si N = 0 Esto significa que, en algún momento del cálculo del factorial, el N pasado por parámetro será cero, y en ese caso la función no se autoinvoca, sino que devuelve el valor 1. A todo esto ya podemos brindar una versión más amplia del contenido de la función factorial( ).

function factorial(n: integer): longint; var aux: longint;

begin

if (n=0) then aux:=1 {CONDICIÓN DE CORTE} else aux:=factorial(n-1)*n {LLAMADA RECURSIVA} factorial:=aux;

end; Vamos a hacer un breve seguimiento de dicha función. Para ello necesitamos dar una noción de cómo se manejan las variables locales a la función durante las llamadas recursivas. Cuando se realizan sucesivas llamadas recursivas todo transcurre de la misma forma que cuando invocamos subprogramas como lo hacemos normalmente. Es decir, en el momento de la invocación se reserva lugar en memoria principal para todas las variables locales al subprograma que estamos llamando. Esa reserva de memoria se administra como una pila, llenándose de la base hacia arriba. Al tratarse de una pila sólo podemos sacar el dato (en este caso la variable local) que está en la cima. En el caso de nuestra función factorial, lo

Page 166: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 166

que se va apilando son las N. Es decir que, después de varias llamadas recursivas tengo una pila, en cuya base está la N correspondiente a la primer llamada a la función, y en la cima está la N correspondiente a la última llamada. Lo importante es destacar que todas las N son distintas, y no crea conflicto el hecho que tengan el mismo nombre, porque son variables locales, y como tales pueden tener el mismo nombre. Básicamente la memoria principal (RAM y ROM) de una PC se divide en las siguientes partes:

La pila de la que hablábamos se crea en el stack. Hagamos el seguimiento de la función factorial. En este caso tenemos sólo dos variables locales: n y aux. Supongamos que queremos calcular el factorial de N=3. La llamada a la función es la siguiente: factorial(3); La primera vez n es igual a 3, pero aux está indeterminado, porque antes de conocer su valor es necesario hacer la llamada recursiva. Entonces la pila en el stack quedaría de la siguiente forma:

Al hacer la llamada recursiva, nuestra nueva variable local n ahora vale 2, pero la nueva variable local aux vuelve a estar indeterminada. El stack queda:

Page 167: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 167

De la misma forma, para las sucesivas llamadas recursivas tendremos las siguientes pilas en el stack:

En este último caso, la nueva variable local n toma el valor 0, y por lo tanto, la nueva variable aux toma el valor 1 (ver el código de la función). Ahora aux está bien determinada, y además se cortan las llamadas recursivas. Entonces, la última invocación a la función devuelve el valor de la variable local aux. ¿De cuál de todas las aux devuelve el valor? De la que está en la cima de la pila (no puedo sacar cosas de la pila que no estén en la cima de la misma). Entonces la última llamada a la función retorna el valor 1. Este valor lo “agarra” la función que la había invocado. El stack que nos queda es el siguiente:

Page 168: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 168

La anteúltima invocación tomó el valor que le retornó la última invocación, o sea 1. Volviendo al código, vemos que en este caso aux queda determinado, y tampoco hay llamada recursiva. Entonces la anteúltima invocación retorna, en este caso, otra vez un 1. Y así sucesivamente se va desandando el camino recorrido, hasta que la pila quede vacía. Los stack siguientes nos quedan de la siguiente forma:

La primer llamada a la función factorial termina devolviendo el valor 6, que es justamente el factorial de 3. El uso de la variable local aux podría evitarse (como más de uno ya lo habrá notado). Acá sólo se utilizó a fines ilustrativos, para demostrar como se comporta el stack. Sólo resta decir, para cerrar el apunte, que existen dos tipos de recursividad: directa e indirecta. La recursividad directa es la que ejemplificamos con la función factorial, y se da cuando el subprograma se invoca a si mismo. La indirecta, en cambio, tiene lugar cuando un

Page 169: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 169

subprograma llama a otro, y éste vuelve a invocar al primero. En ambos casos es indispensable incluir una condición de corte. También cabe aclarar que todo proceso recursivo puede implementarse de manera iterativa. Es decir, podríamos haber calculado el factorial de un número sin necesidad de invocar a otros subprogramas, y utilizando algún tipo de ciclo iterativo (por ejemplo un ciclo for). Queda como tarea construir una función que calcule el factorial de N dentro de sí misma, en forma iterativa (sin llamadas recursivas). Aquí finaliza el apunte sobre recursividad. El tema puede ampliarse, y realmente tiene gran aplicación. Otro ejemplo clásico es el cálculo de los números de Fibonacci.

Page 170: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 170

RECURSIVIDAD (EJEMPLOS) Ejemplo 1 Construir una función recursiva que calcule el factorial de un número. El factorial de N se calcula de la siguiente forma: 1 si N = 0 FACT(N) = FACT(N-1) * N si N > 0 program fact; uses wincrt; var numero: integer; function factorial(n: integer): longint; begin if (n=0) then factorial:=1 {CONDICION DE CORTE} else factorial:=factorial(n-1)*n {LLAMADA RECURSIVA} end; begin {Prog. Ppal.} clrscr; write('Ingrese un numero: '); readln(numero); writeln; writeln('El factorial de ', numero, ' es = ', factorial(numero)); readkey; end. Ejemplo 2

Page 171: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 171

Construir una función recursiva que calcule los números de Fibonacci para un N dado. La función tiene la siguiente forma: 0 si N = 0 FIBONACCI(N) 1 si N = 1 FIBONACCI(N-2) + FIBONACCI(N-1) si N ≥ 2 program numeros_fibonacci; uses wincrt; var numero: integer; function fibonacci(n: integer): longint; begin if (n>=2) then fibonacci:=fibonacci(n-1)+fibonacci(n-2) {LLAMADA RECURSIVA} else fibonacci:=n; {CONDICION DE CORTE} end; begin clrscr; write('Ingrese un entero: '); readln(numero); writeln; writeln('La serie de Fibonacci da como resultado: ', fibonacci(numero)); readkey; end. Ejemplo 3 Dado el siguiente procedimiento recursivo, decir qué es lo que realiza. program acertijo;

Page 172: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 172

uses wincrt; procedure XXX; var c: char; begin c:=readkey; write(c); if (c<>'.') then XXX; if (c<>'.') then write(c); end; begin clrscr; XXX; readkey; end. RTA: Rebate una cadena de caracteres ingresada por teclado.

Page 173: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO XI

Manejo de Punteros y Memoria Dinámica

Page 174: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 174

MANEJO DE PUNTEROS Y MEMORIA DINÁMICA

Diferenciación entre variables estáticas y dinámicas Así como habíamos clasificado a las variables en atómicas y estructuradas, también se las puede clasificar en estáticas y dinámicas. Las variables estáticas

Son las variables que conocemos hasta ahora. Las declaramos con var al principio del procedimiento o programa que estamos haciendo. Estas se crean en la memoria antes de ser ejecutado el programa. El espacio que ocupa es siempre fijo y puede ser calculado simplemente leyendo las declaraciones. La memoria se divide en varios segmentos según veremos; estas variables se encontrarían almacenadas dentro del segmento de datos (data segment). Por ejemplo:

Type tCadena=string[30];

Var foo:tCadena; bar:tCadena;

En este caso la memoria consumida por nuestro programa será de 60 caracteres, de los cuales 30 le pertenecen a la variable foo y otros 30 a la variable bar. Esto es bastante simple pero trae muchas desventajas. Por ejemplo si queremos almacenar una cadena de 40 caracteres tenemos que recompilar el programa, es decir que -al trabajar con variables estáticas- no es posible editar el tamaño de la memoria en tiempo de ejecución, sino que tenemos que hacerlo en tiempo de diseño. Es decir que no se puede agrandar el tamaño de un array o de un string mientras se está corriendo el programa. Otra desventaja que acarrean las variables estáticas es el uso excesivo de memoria; es decir que si a mi variable foo de tipo tCadena la voy a llenar con solo 10 caracteres, los 20 caracteres restantes estarían ocupando espacio no aprovechado en memoria. O lo mismo si tengo un array de 10 000 enteros y solo uso los primeros 5, en este caso me quedarían 9 995 veces el tamaño de un entero sin utilizar. Es por eso que al trabajar con variables estáticas como arreglos, registros o strings a los cuales se les puede definir un tamaño -las variables estáticas son de todos los tipos, pero solo mencioné arreglos y strings porque el tamaño uno lo elige en la declaración- es muy importante evaluar sobre el uso del programa para decidir bien el tamaño de las variables. Al ejecutarse el programa, como este no conoce el tamaño que tendrán las variables que le serán ingresadas, reservará en memoria el tamaño fijado en las declaraciones de tipos y variables. Esto puede ser muy ineficiente a la hora de ahorrar memoria.

Page 175: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 175

Las variables dinámicas

Por suerte Pascal admite variables dinámicas, aunque estas tienen otro modo de ser utilizadas. En tiempo de ejecución deben ser creadas y/o eliminadas. Esto hace que existan dos procedimientos en Pascal indispensables para este tipo de variables. Además al no ser declaradas, estas variables carecen de nombre: simplemente son un espacio de memoria que se reserva y que luego se devuelve. Por eso pueden ser llamadas anónimas. Con este tipo de variables se pueden formar estructuras de datos bastante complejas y modificables en tamaño, capaces de ocupar la mínima cantidad de memoria que realmente necesitan para funcionar –estas estructuras pueden ser árboles, listas, pilas, colas, grafos, etc-. Cuando definimos un puntero, debemos especificar a qué tipo de datos apunta; esto se debe a que cada tipo de datos puede ocupar un tamaño distinto en la memoria. Al no poseer un nombre, para poder referirse a estas variables es necesario otro tipo de variable llamada puntero. Este tipo -que puede ser a su vez dinámico o estático- es atómico y contiene como dato la primera posición de memoria en la cual se encuentra la variable dinámica a la que queremos acceder. De esta forma, no sabemos el nombre de nuestra variable, pero sí sabemos donde se encuentra y por lo tanto como accederla. Este es el concepto de puntero, apuntador o pointer, sin embargo no vamos a ver en este curso como se calculan posiciones de memoria ni nada por el estilo, puesto que el mismo Pascal asigna la posición al puntero siendo transparente para el programador. Es muy importante recordar que al terminar de utilizar una variable dinámica, su espacio en memoria debe ser liberado, ya que de lo contrario queda asignado ocupando memoria que no puede ser utilizada por otros procesos. El lugar de la memoria en el cual se almacenan estas variables no es el mismo que el que utilizan las variables estáticas para almacenarse. Las variables dinámicas se almacenan en el heap o pila dinámica. El siguiente esquema representa un puntero estático llamado punt (almacenado en el data segment), apuntando a una porción de memoria dinámica (almacenada en el heap), cuya dirección es 1003.

STACK

CODESEGMENT

1003

HOLA, MUNDO.1003

HEAP

DATASEGMENT

PUNT

Page 176: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 176

Notar que el contenido del puntero punt es la dirección de la porción de memoria a la cual apunta. Para acceder a esa porción de memoria, derreferenciamos el puntero de la siguiente forma: punt^ Sintaxis y utilización de punteros en Pascal Declaración de punteros

En el uso de memoria dinámica aparecen en escena dos variables: una es el puntero -que contiene la posición de memoria- y la otra es variable apuntada -lo que hay en la posición de memoria. El tipo puntero se declara mediante un acento circunflejo (^) seguido por el tipo al cual apunta. Prácticamente todo tipo de dato puede ser apuntado por un puntero, incluidos los mismos punteros; las únicas excepciones son los tipos relacionados a archivos, es decir file of y text. Por ejemplo:

var foo: ^tCadena; En este ejemplo, foo es una variable de tipo puntero capaz de apuntar a cualquier posición que contenga una variable del tipo tCadena. La cadena que se encuentra en la posición de memoria a la cual apunta foo es representada por foo^. Procedimientos para crear y eliminar punteros

En Pascal, para asignar un espacio en la memoria a una variable dinámica mediante punteros se utiliza el procedimiento new, mientras que para liberar el espacio, el procedimiento que se debe utilizar es dispose. Procedimiento New Sintaxis:

new(foo); Este procedimiento asigna al puntero foo una posición de memoria, reservándola para que almacene una variable del tipo especificado en la declaración de foo. Procedimiento Dispose Sintaxis:

dispose(foo);

Page 177: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 177

Libera la posición de memoria ocupada por la variable dinámica a la que apuntaba foo y se la devuelve al heap. A partir de ahora, dicha posición queda disponible para que pueda ser utilizada por otros procesos. Acceso a datos y asignación de punteros Acceso a valores de variables dinámicas Es importante saber diferenciar cuando se asigna una variable puntero y cuando se asigna el dato al cual apunta. En la asignación de punteros uno está asignando solamente la dirección de memoria dejando el dato intacto, mientras que cuando uno asigna la variable a la cual apunta está cambiando el dato en el heap. Todo valor “apuntado” por un puntero puede ser accedido mediante el nombre del puntero seguido del circunflejo. Supongamos en los siguientes ejemplos que foo es una variable de tipo puntero que apunta al puntero bar, la cual también es una variable de tipo puntero, pero que apunta a un string de tamaño 30 cuyo valor es ‘hola mundo’.

writeln(bar^); {Escribe en pantalla 'hola mundo'} writeln(bar^[6]); {Escribe en pantalla 'm'} writeln(foo^^); {Escribe en pantalla 'hola mundo'} En el ejemplo que sigue foo es una variable tipo puntero que apunta a un registro que contiene un string apellido y un longint padrón.

writeln(foo^.nombre); writeln(foo^.padron);

Asignación de valores Sintaxis:

foo^ := 'hola'; Asigna el valor 'hola' a la posición de memoria a la cual apunta foo. Análogamente su utilización coincide con la de los ejemplos explicados anteriormente. Asignación de posiciones de memoria Sintaxis:

foo := bar;

Page 178: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 178

Condición: foo y bar son punteros al mismo tipo de datos. Asigna la posición de memoria apuntada por bar al puntero foo (y ambos pasan a apuntar a la misma porción de memoria dinámica).

STACK

CODESEGMENT

1003

HOLA, MUNDO.1003

HEAP

BAR

1003FOO

Es muy importante realizar la siguiente aclaración: si antes de la asignación, foo apuntaba a una cierta posición de memoria reservada, esta quedará sin liberarse (y nunca más podremos liberarla, ya que hemos perdido todo rastro de ella). Por esta razón, de estar foo apuntando a algo antes de la asignación, deberíamos utilizar el siguiente método:

dispose(foo); foo := bar;

El siguiente gráfico ilustra el caso en que se realiza la asignación, sin antes liberarse la memoria apuntada por foo.

STACK

CODESEGMENT

1003

HOLA, MUNDO.1003

BAR

1060FOO

PIRULO.1060

STACK

CODESEGMENT

1003

HOLA, MUNDO.1003

BAR

1003FOO

PIRULO.1060

f o o : = b a r ;

Page 179: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 179

Constante NIL

Existe en Pascal un valor que puede tomar un puntero de cualquier tipo y que no corresponde a una posición de memoria. Es una constante predefinida llamada nil y se suele utilizar en punteros que no apuntan a ningún lado. Aplicación de punteros A esta altura del apunte ya se deben preguntar para qué declarar una variable que apunte hacia cierto lugar, cuando es posible declararla directamente en tiempo de edición. Pero la razón es que los punteros permiten generar estructuras de datos capaces de variar mientras el programa se ejecuta (en tiempo de ejecución); estas son compuestas principalmente por un registro que contiene uno o varios punteros adentro que apuntan a registros del mismo tipo. Por ejemplo: {Este ejemplo es la declaración de una estructura de datos llamada "lista" que contiene una sucesión de registros de cadenas de texto del tipo tCadena}

type tCadena = string; tElementoLs = tCadena; {este es el tipo elemento,

es decir un elemento de la lista}

tPLsNodo = ^tLsNodo; {este es el tipo puntero del

elemento} tLsNodo = record {este es el nodo, es decir un

registro con un puntero a otro nodo y un elemento de la lista}

siguiente: tPLsNodo; elemento: tElementoLs;

end; tLs = record {este es el tipo Lista, que

simplemente contiene un puntero al primer nodo y otro a la posición actual donde se encuentra el cursor (este segundo es un detalle de implementación)}

prim, corriente: tPLsNodo;

end;

Page 180: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 180

Analizando el ejemplo podemos graficar lo siguiente:

Detalles de implementación y otras estructuras de datos escapan a esta materia y se verán con más profundidad en cursos posteriores. Principalmente las estructuras que se pueden generar son arboles, grafos, listas, pilas y colas. Aquí finaliza este enfoque introductorio al concepto de memoria dinámica y manejo de punteros. En particular, nuestro ámbito de trabajo será el lenguaje Pascal, aunque todo lo visto en este apunte puede aplicarse, diferencias mediante, a otros lenguajes de programación (como el lenguaje C).

Lista (tLs)

Nodo (tLsNodo) Nodo (tLsNodo) Nodo (tLsNodo) Constante

NIL Elemento Elemento Elemento

Prim

Page 181: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 181

MANEJO DE PUNTEROS Y MEMORIA DINÁMICA (EJEMPLOS)

Ejemplo 1 Autor: Alejandro Zylberberg Sea el siguiente programa: program punteros; uses crt; type puntero = ^nodo; nodo = record info: integer; izquierda, derecha: puntero; end; var a,b,c: puntero; begin

new(a); new(b); new(c); a^.info:=1; b^.izquierda:=a; c^.derecha:=b; a^.izquierda:=c; b^.derecha:=c; c^.info:=3; a^.derecha:=b; b^.info:=5; c^.izquierda:=a; a^.izquierda:=b^.derecha; a^.izquierda^.izquierda:=a; a^.derecha:=b^.izquierda^.derecha; c^.derecha:=b^.izquierda^.izquierda; a:=b; c^.izquierda^.info:=4;

clrscr; writeln(a^.info,b^.info,c^.info);{Imprime en pantalla

553} readkey;

Page 182: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 182

end. Veremos, paso por paso, cómo se llega al resultado: program punteros; uses crt; Eso empieza un nuevo programa y usa la librería CRT. type Tpuntero = ^nodo;

nodo = record info: integer; izquierda, derecha: Tpuntero;

end; Eso crea dos tipos de datos: Tpuntero y nodo. Tpuntero es un puntero a un elemento de tipo nodo. El tipo nodo es un registro que contiene un campo info, de tipo integer, y dos campos, izquierda y derecha, que su vez son punteros a otros elementos. Un detalle a destacar de esto es que estamos definiendo Tpuntero como un puntero a “nodo” antes de definir “nodo”. Esta excepción es permitida debido a casos como este, donde es necesario que un elemento apunte a otro elemento. Por ejemplo, en una lista, cada elemento tiene que a puntar al siguiente. var a,b,c: puntero; Eso crea tres punteros capaces de apuntar a un elemento de tipo nodo. begin

new(a); new(b); new(c);

Eso crea tres espacios en memoria que pueden contener elementos de tipo nodo, y que a su vez están apuntados por los punteros a, b y c. Si a esos espacios los llamamos 1, 2 y 3 respectivamente, entonces podríamos representar lo que queda en la memoria de esta forma:

Posic Info Izquierda Derecha a 1 b 2 c 3

Page 183: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 183

Es decir, tres registros en la memoria, en las direcciones 1, 2 y 3, y apuntadas, respectivamente, por los punteros a, b y c. a^.info hace referencia al campo info del elemento apuntado por a. a^.izquierda hace referencia a la dirección izquierda del elemento apuntado por a. a^.derecha hace referencia a la dirección derecha del elemento apuntado por a. a (solamente el nombre del puntero) hace referencia al bloque de memoria apuntado por a. Esto lo veremos claramente más adelante. Por ahora continuemos con el seguimiento del programa: a^.info:=1; Eso carga un 1 en el campo info del elemento apuntado por a. La memoria queda así:

Posic Info Izquierda Derecha a 1 1 b 2 c 3

Sigamos: b^.izquierda:=a; Eso pone en el campo izquierda del elemento apuntado por b, la dirección apuntada por a. La memoria queda así:

Posic Info Izquierda Derecha a 1 1 b 2 1 c 3

Entonces en izquierda de b quedó la dirección del bloque apuntado por a, es decir, 1. Continuamos: c^.derecha:=b; a^.izquierda:=c; b^.derecha:=c; c^.info:=3; a^.derecha:=b; b^.info:=5; c^.izquierda:=a;

Page 184: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 184

Esas instrucciones se resuelven análogamente. Luego de ellas, la memoria queda así:

Posic Info Izquierda Derecha a 1 1 3 2 b 2 5 1 3 c 3 3 1 2

Veamos la siguiente sentencia: a^.izquierda:=b^.derecha; Eso le asigna a la dirección izquierda de a, la dirección derecha de b. Es decir, escribirá un 3 donde ya había un 3, con lo cual no cambia nada. a^.izquierda^.izquierda:=a; Esto puede parecer un trabalenguas, pero lo que hace es lo siguiente: le asigna a la dirección izquierda del elemento que está en la dirección izquierda de a, la dirección apuntada por a. Es decir, escribirá un 1 donde ya había un 1, con lo cual no cambia nada. a^.derecha:=b^.izquierda^.derecha; Otro trabalenguas: a la dirección derecha de a, le asigna la dirección derecha del elemento que está en la dirección izquierda de b. Es decir, escribirá un 2 donde ya había un 2, con lo cual no cambia nada. c^.derecha:=b^.izquierda^.izquierda; Esto, a la dirección derecha de c, le asigna la dirección izquierda del elemento que está en la dirección izquierda de b, con lo cual la memoria queda así:

Posic Info Izquierda Derecha A 1 1 3 2 B 2 5 1 3 C 3 3 1 3

Sigamos: a:=b;

Page 185: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 185

Eso a la dirección apuntada por a, le asigna la dirección apuntada por b, con lo cual a y b apuntarán a lo que antes apuntaba b. La memoria queda así:

Posic Info Izquierda Derecha a 1 1 3 2 b 2 5 1 3 c 3 3 1 3

Notemos que ahora nadie apunta al bloque de dirección 1. Eso hace que dicho bloque de memoria quede inaccesible. Es decir, ese sector de la memoria seguirá conteniendo la información, pero ya no se podrá acceder a él. Es decir, queda ocupando un sector de la memoria, pero no podemos usarlo (para ver cómo hacer para que no queden sectores inaccesibles, ver la explicación de la instrucción dispose). Sigamos:

c^.izquierda^.info:=4; Eso escribe un 4 en la info del elemento que está en la dirección izquierda de c. La memoria queda así:

Posic Info Izquierda Derecha a 1 4 3 2 b 2 5 1 3 c 3 3 1 3

Notemos que acabamos de escribir en una dirección a la cual nadie le apunta. Sigamos:

clrscr; writeln(a^.info,b^.info,c^.info); readkey;

end. Eso escribe las info de los bloques apuntados por a, b y c en ese orden. Entonces se imprime:

5 5 3

Page 186: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 186

Ejemplo 2 Se aloca un puntero a entero, luego se le asigna el número 15, y finalmente se libera esa memoria. program EjemploPunteros; uses crt; var PInteger: ^integer; begin clrscr; new(PInteger); PInteger^ := 15; writeln('El numero apuntado por p es = ', PInteger^); dispose(PInteger); readkey; end. Ejemplo 3 Se aloca un puntero a entero (PInteger), luego se le asigna el número 15. Se tiene otro puntero (PInteger2) que “apunta” al mismo bloque de memoria que PInteger. Se modifica lo apuntado por PInteger2 (que es lo mismo que lo apuntado por PInteger). Luego se libera la memoria. program EjemploPunteros; uses crt; var PInteger, PInteger2: ^integer; begin clrscr; new(PInteger); PInteger^ := 15; writeln('El numero apuntado por PInteger es = ',

PInteger^); PInteger2 := PInteger; PInteger2^ := 20; writeln('El numero apuntado por PInteger es = ',

PInteger^); writeln('El numero apuntado por PInteger2 es = ',

PInteger2^);

Page 187: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 187

dispose(PInteger); { ¿ No habria que hacer un dispose de PInteger2 ? ¿ Por

qué ? } readkey; end. Ejemplo 4 Se aloca un puntero a caracter si el puntero es igual a NIL (“puntero a nada”). Luego se le asigna una letra, y finalmente se libera la memoria. program EjemploPunteros; uses crt; var PChar: ^char; begin clrscr; PChar := NIL; if (PChar = NIL) then new(PChar); PChar^ := 'A'; writeln('El numero apuntado por PChar es = ', PChar^); dispose(PChar); readkey; end. Ejemplo 5 En este ejemplo se muestra un “error típíco” cuando se comienza a trabajar con punteros. Alocamos memoria para un puntero (PChar), y luego le asignamos NIL, con lo cual perdemos la referencia a la memoria reservada, y por consiguiente no podremos liberarla nunca más (referencia “colgada”). program EjemploPunteros; uses crt; var PChar: ^char;

Page 188: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 188

begin clrscr; {Alocamos memoria para PChar, y PChar pasa a contener

una direccion} new(PChar); {Asignamos NIL a PChar, pisando la direccion que

contiene} PChar := NIL; {Intentamos liberar la memoria apuntada por PChar, pero

PChar no apunta a ningun bloque de memoria, porque le asignamos NIL.}

dispose(PChar); readkey; end. Ejemplo 6 PString es un puntero a string, y por lo tanto nos permite reservar memoria para una cadena de caracteres en tiempo de ejecución. Le asignamos una cadena de caracteres (‘HOLA’), y finalmente lo liberamos. program EjemploPunteros; uses crt; type tPString = ^string; {Declaracion del tipo puntero a

string.} var PString: tPString; {Declaracion de la variable puntero a

string.} begin clrscr; {Alocamos memoria para PString, y PString pasa a

contener una direccion de memoria.} new(PString); {Asignamos una cadena de caracteres a la direccion

apuntada por PString.} PString^ := 'HOLA';

Page 189: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 189

writeln('La cadena apuntada por PString es = ',

PString^); {Liberamos la memoria apuntada por PString.} dispose(PString); readkey; end.

Page 190: Apunte Teorico(AlgoritmosI)(VERSION3)

CAPÍTULO XII

Unidades de Biblioteca

Page 191: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 191

UNIDADES DE BIBLIOTECA

A medida que los desarrollos que necesitamos realizar cobran mayor complejidad, se hace necesario contar con “bibliotecas” de código ya realizado, para usar en las ocasiones que sea necesario. Aún más, pueden conseguirse soluciones desarrolladas por terceros, a fines de ser utilizadas en nuestros programas. Es en auxilio de estas necesidades que aparecen las llamadas “unidades de biblioteca” o “units”. En el siguiente texto intentaremos contestar tres preguntas fundamentales: ¿Qué son?, ¿Para qué sirven?, ¿Cómo se hacen? Primer Pregunta: ¿ Qué es una unit ? Una unit consiste en un conjunto de tipos, procedimientos, funciones, constantes y eventualmente variables, que pueden ser invocados desde otro programa o unidad que la referencie. En otras palabras, es un “pedazo” del programa que uno realiza, que ya se encuentra hecho de antemano y uno utiliza. Generación de un archivo ejecutable, a partir de un código que no usa units Definiciones preliminares:

- Código fuente: programa escrito por el programador en el lenguaje elegido (por ejemplo Pascal)

- Compilador: encargado de traducir el código fuente a otro de más bajo nivel, conocido como objeto.

- Código objeto: código generado por el compilador, que no puede ser ejecutado (contiene algunos datos más que un ejecutable), pero tampoco es entendible por un ser humano.

- Linker: encargado de buscar todas las partes que conforman el programa (el programa principal y las unidades de biblioteca que utiliza) y armar con ellas el ejecutable (combina los módulos para formar el ejecutable, que está en código de máquina). También reemplaza direcciones simbólicas por reales, por eso los programas compuestos de un solo módulo también se linkean.

- Archivo ejecutable: archivo (de extensión .exe si la plataforma es DOS/Windows) capaz de ser ejecutado por un usuario, generado por el linker.

Veamos el camino que sigue un código fuente, que no utiliza unidades de biblioteca, hasta llegar a ser un archivo ejecutable.

Page 192: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 192

El código escrito en el lenguaje de programación (en nuestro caso Pascal) es tomado por el compilador quien lo traduce a código objeto. Luego, el linkeador se encarga de traducirlo a lenguaje de máquina, para la plataforma en la que se esté trabajando. Generación de un archivo ejecutable, a partir de un código que usa units Las unidades de biblioteca se encuentran en código objeto. Es decir, que la unidad se encuentra compilada, aunque no puede ser ejecutada independientemente. Estas librerías se incorporan al archivo ejecutable durante el proceso de linkeo, a diferencia de lo que sucede con las librerías dinámicas como las .DLL del entorno Windows, que son interpretadas en tiempo real. Veamos que sucede al utilizar unidades de biblioteca con un gráfico similiar al anterior:

Puede verse que lo que sucede es que se une (linkea) al programa con las unidades que éste utiliza, generando el archivo ejecutable (.exe). Segunda Pregunta: ¿ Para qué sirve ? Una unidad de biblioteca permite contar con soluciones a necesidades de código listas para usar, ya testeadas y adaptables al programa que se encuentra en desarrollo. Existen numerosas ventajas en la utilización de units, que se desciben a continuación.

Linkeador Compilador

Archivo .PAS

Código Objeto

Ejecutable

Linkeador Compilador

Archivo .PAS

usa Unid1 usa Unid2

Código Objeto

Ejecutable

Unid1 (.TPU)

Unid2 (.TPU)

Page 193: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 193

1. Bien sabida es la ventaja de separar adecuadamente los programas en módulos de acuerdo a su funcionalidad, y de la necesidad que lo mismos sean independientes. Cada módulo debe ser capaz de funcionar adecuadamente en una variedad de contextos, siempre y cuando se respeten las condiciones indispensables que estos requieran (precondiciones). Las unidades de biblioteca permiten tener un repositario de módulos ya testeados, listos para ser usados en otro programa.

2. Justamente, al contar con módulos independientes listos para usar, los mismos

pueden ser invocados desde varios programas. 3. La separación en unidades de biblioteca permite dividir las tareas entre los

integrantes de un equipo de desarrollo, ya que cada uno puede desarrollar con libertad su parte respetando sólo pautas comunes básicas.

4. Dada la característica de las unidades de bibliotea de encontrarse ya compiladas, si

no se distribuye el código fuente al distribuir una unit, será imposible conocer su implementación y por ende se protegerá la propiedad intelectual de quien la desarrolló.

5. En la solución de problemas comunes, puede recurrirse a código ya desarrollado por

otros programadores, ahorrando tiempo en la concreción del proyecto en curso. Tercer Pregunta: ¿ Cómo se hace ? Definiciones preliminares:

Page 194: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 194

- Declaración: consiste en el encabezado de un procedimiento o función, que dice cuántos parámetros y de qué tipo tomará, y qué devuelve (si devuelve algo), lo que se conoce como prototipo de la función (o procedimiento).

Por ejemplo: function suma (num1, num2 :integer) : integer; - Definición: consiste en la declaración de la función (o procedimiento) más el código

de implementación de la misma (el cuerpo de la función). Por ejemplo: function suma (num1, num2 :integer) : integer; begin suma:= num1+num2; end;

Una unidad de biblioteca cuenta con cuatro secciones principales, que conforman su estructura. Estructura de una unit: Sección de CABECERA Sección de INTERFAZ Sección de IMPLEMENTACION Sección de INICIALIZACION Sección de cabecera: Esta corta sección consta solamente de la palabra reservada unit seguida del nombre que se da a la unidad. Por ejemplo: Unit cuentas; Aclaración: el nombre del archivo en que se grabará la unit debe coincidir con el de la misma, es decir que el código fuente de esta unidad deberá estar en un archivo cuentas.pas. Esto impone una restricción ya que, al estar Pascal basado en DOS, este nombre no podrá superar los 8 caracteres.

Page 195: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 195

Existe una manera de sortear este inconveniente, al hacer la llamada desde el programa que invoca la unidad. Sección de interfaz: Esta sección es “visible” desde fuera de la unidad. Esto quiere decir que el usuario de la unidad puede invocar lo que figura y sólo lo que figura aquí. La sección de interfaz puede contener constantes, tipos, variables, procedimientos y funciones. Debe destacarse sin embargo que en el caso de procedimientos y funciones lo que figura es la declaración ya que, como se verá, luego se definen en la implementación. Es decir, en esta sección se encuentra todo lo que se exporta hacia el exterior. La sección de interfaz comienza con la palabra reservada interface y se extiende hasta la sección de implementación. De esta manera la siguiente podría ser una interfaz de la unidad de cuentas: Interface Const ERROR = maxint; function suma (num1, num2: integer): integer; function resta(num1, num2: integer): integer; function multi(num1, num2: integer): integer; function divi (num1, num2: integer): real; Se ve que lo que figura es la declaración de cada función. Sección de implementación: Esta sección también contiene constantes, variables, tipos, funciones y procedimientos, con la salvedad que no son vistos desde afuera. Aquí se encuentran todas las definiciones de los procedimientos y funciones, de los cuales sólo podran ser invocados aquellos que figuren en la sección de interfaz. La sección de implementación comienza con la palabra reservada implementation. Ejemplo: implementation function suma(num1, num2: integer): integer; begin suma:=num1 + num2; end;

Page 196: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 196

function resta(num1, num2: integer): integer; begin resta:=num1 - num2; end; function multi(num1, num2: integer): integer; begin multi:=num1 * num2; end; function esCero (numero: integer): boolean; begin if numero=0 then esCero:=true else esCero:=false; end; function divi (num1, num2: integer): real; begin if not esCero(num2) then divi:=num1 / num2 else divi:= ERROR; end; Con esta sección de implementación, el usuario podrá realizar las operaciones de suma, resta, multiplicación y división entre números enteros. Debe notarse que las sección en rojo no se encuentra en la interfaz. Por ende el usuario no podrá usar la función esCero, sino que sólo los procedimientos y funciones de la unidad pueden hacerlo. Sección de inicialización: Esta sección es análoga a un programa principal. Comienza con begin y termina con end. Aunque si está vacía puede ponerse sólo end. El código de esta sección se ejecuta una sola vez, antes que cualquier instrucción del programa que llama a la unidad. En nuestro ejemplo será:

end. Compilación de la unidad La unidad de biblioteca escrita en código fuente debe ser compilada. Para ello, en nuestro caso le indicaremos al entorno Turbo Pascal que deseamos compilar a disco, y luego compilaremos. Se generará un archivo en código objeto, de extensión .tpu (cuentas.tpu) en el directorio que se haya indicado en las opciones de configuración.

Page 197: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 197

Invocación de la unidad La unidad generada puede ser invocada desde un programa, o bien desde unidades, externos. Debe indicarse en la cláusula uses que se usará dicha unidad, junto con todas las otras que se utilizarán. En nuestro caso: uses cuentas, crt; Luego las funciones, procedimiemtos, constantes, tipos y variables visibles desde el programa (es decir, que están en la interfaz) podrán ser invocados normalmente, por ejemplo para sumar dos números se hará:

suma(a, b) siendo a y b los números a sumar. Ejemplo: program hace_cuentas; uses cuentas, crt; var a, b: integer; division: real; begin clrscr; writeln('Ingrese los valores de los numeros'); readln(a, b); writeln('Suma: ',suma(a, b)); writeln('Resta: ',resta(a, b)); writeln('Producto: ', multi(a, b)); division:=divi(a,b); if division<>ERROR then

writeln('Cociente: ', division:7:2) else

writeln('Error al dividir'); readkey; end.

Page 198: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 198

Debe notarse que si se trata de llamar a esCero se tiene un error en tiempo de compilación, ya que el programa no conoce tal función. Sí es posible, en cambio, utilizar la constante ERROR, ya que ésta se encuentra en la interfaz. Sobre utilización de units Al indicar que se usará una unidad de biblioteca, ésta debe encontrarse en el directorio actual, en el que se encuentra el archivo turbo.tpl o en el definido para las mismas en las opciones de configuración, de manera que el programa pueda saber dónde encontrar el archivo .tpu correspondiente a la unidad. Sobre la invocación de los contenidos Es posible (y muchas veces recomendable) especificar a qué unidad pertenece la constante, el tipo, la función o procedimiento del que se hace uso. Es decir, en vez de la línea: writeln('Suma: ',suma(a, b)); pudo haberse escrito writeln('Suma: ', cuentas.suma(a, b)); Es decir, que en rigor de verdad la invocación tiene la forma Unidad.Contenido. Esto no fue necesario en nuestro ejemplo, pero sería imperativo si se tuviera una unidad cuentas1 y otra cuentas2, una para enteros y otra para reales, y ambas tuvieran funciones suma, resta, etc. De todas formas, aporta claridad incluirlo aunque no sea estrictamente indispensable. Nota: Si se omitiera la aclaración en un caso necesario, se invocaría a la función o procedimiento correspondiente a la última unidad incluida en la cláusula uses, convirtiendo en una cuestión de suerte que corresponda o no a la que el programador quiere invocar. Si el nombre del archivo fuera distinto al de la unidad, en la cláusula uses debería usarse la sintaxis Uses {$archivo} nombre; donde archivo es el nombre del archivo (sin ruta ni extensión) y nombre el nombre de la unidad.

Page 199: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 199

Compile vs. Make Al compilar un programa, se buscan los archivos .TPU ya compilados y se incorporan al programa. En cambio, al elegir la opción make, se recompilan los archivos .pas de las unidades (siempre que estén disponibles). Referencias circulares Se ha mencionado que la unidad se compila antes que el programa o unidad que la utiliza. Por lo tanto, dos unidades no pueden utilizarse entre si, ya que no sería posible determinar cuál compilar primero.

Page 200: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 200

EJEMPLO DE MANEJO DE UNIDADES DE BIBLIOTECA

A continuación presentamos un ejemplo de aplicación de units, que está también estrechamente relacionado con el capítulo XI: “Manejo de Punteros y Memoria Dinámica”. Consiste en un conjunto de primitivas que permiten manejar listas dinámicas constituídas en base a punteros, y simplemente enlazadas (es decir que cada nodo de la lista apunta al siguiente nodo, pero no apunta al anterior). unit lista; interface const PUNTERO_NULO = NIL; type tDato = integer; { Elemento contenido por cada nodo } tPtrNodo = ^tNodo; { Puntero a un nodo de la lista } tNodo = record { Nodo de la lista } Dato: tDato; Siguiente: tPtrNodo; end; tLista = tPtrNodo; { Lista } {PRE: Ninguna. POST: Crea una nueva lista.} procedure CreaLista(var Lista: tLista); {PRE: La lista fue creada con CreaLista(). POST: Devuelve TRUE si la lista está vacía, o FALSE en caso contrario.} function ListaVacia(Lista: tLista): boolean; {PRE: La lista fue creada con CreaLista(). POST: Devuelve un puntero al primer nodo de la lista, o PUNTERO_NULO si la lista está vacía.} function PrimerNodo(Lista: tLista): tPtrNodo;

Page 201: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 201

{PRE: La lista fue creada con CreaLista(), y NodoActual apunta a un nodo de la lista. POST: Devuelve un puntero al nodo siguiente a NodoActual, o PUNTERO_NULO en caso que NodoActual sea el último de la lista.} function SiguienteNodo(Lista: tLista; NodoActual: tPtrNodo): tPtrNodo; {PRE: La lista fue creada con CreaLista(), NodoActual apunta a un nodo de la lista, y NodoActual no es el primer nodo de la lista. POST: Devuelve un puntero al nodo anterior a NodoActual.} function AnteriorNodo(Lista: tLista; NodoActual: tPtrNodo): tPtrNodo; {PRE: La lista fue creada con CreaLista(). POST: Devuelve un puntero al último nodo de la lista, o PUNTERO_NULO si la lista está vacía.} function UltimoNodo(Lista: tLista): tPtrNodo; {PRE: Nodo es un puntero a un nodo de la lista. POST: Devuelve en Dato el dato contenido en el nodo apuntado por Nodo.} procedure LeeDato(Nodo: tPtrNodo; var Dato: tDato); {PRE: La lista fue creada con CreaLista(). POST: Agrega un nuevo nodo al principio de la lista, cuyo dato es Dato.} procedure AgregaPrincipio(var Lista: tLista; Dato: tDato); {PRE: La lista fue creada con CreaLista() y NodoActual apunta a un nodo de la lista. POST: Agrega un nuevo nodo luego de NodoActual, cuyo dato es Dato.} procedure AgregaDespues(var Lista: tLista; NodoActual: tPtrNodo; Dato: tDato); {PRE: La lista fue creada con CreaLista() y no está vacía, y Nodo apunta a una nodo de la lista. POST: Elimina el nodo apuntado por Nodo de la lista.} procedure EliminaNodo(var Lista: tLista; Nodo: tPtrNodo); {PRE: La lista fue creada con CreaLista(). POST: Elimina la lista.} procedure EliminaLista(var Lista: tLista);

Page 202: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 202

implementation procedure CreaLista(var Lista: tLista); begin Lista := PUNTERO_NULO; end; function ListaVacia(Lista: tLista): boolean; begin ListaVacia := (Lista = PUNTERO_NULO); end; function PrimerNodo(Lista: tLista): tPtrNodo; begin PrimerNodo := Lista; end; function SiguienteNodo(Lista: tLista; NodoActual: tPtrNodo): tPtrNodo; begin SiguienteNodo := NodoActual^.Siguiente; end; function AnteriorNodo(Lista: tLista; NodoActual: tPtrNodo): tPtrNodo; var PtrAux: tPtrNodo; begin PtrAux := Lista; while ( (PtrAux^.Siguiente <> NodoActual) and

(PtrAux^.Siguiente<>PUNTERO_NULO) ) do begin PtrAux := PtrAux^.Siguiente; end; if ( PtrAux^.Siguiente = PUNTERO_NULO ) then

Page 203: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 203

AnteriorNodo := PUNTERO_NULO else AnteriorNodo := PtrAux; end; function UltimoNodo(Lista: tLista): tPtrNodo; var PtrAux: tPtrNodo; begin if ( ListaVacia(Lista) ) then UltimoNodo := PUNTERO_NULO else begin PtrAux := Lista; while (PtrAux^.Siguiente <> PUNTERO_NULO) do begin PtrAux := PtrAux^.Siguiente; end; UltimoNodo := PtrAux; end; end; procedure LeeDato(Nodo: tPtrNodo; var Dato: tDato); begin Dato := Nodo^.Dato; end; procedure AgregaPrincipio(var Lista: tLista; Dato: tDato); var PtrAux: tPtrNodo; begin new(PtrAux); PtrAux^.Dato := Dato; PtrAux^.Siguiente := Lista; Lista := PtrAux; end; procedure AgregaDespues(var Lista: tLista; NodoActual: tPtrNodo; Dato: tDato);

Page 204: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 204

var PtrAux: tPtrNodo; begin new(PtrAux); PtrAux^.Dato := Dato; PtrAux^.Siguiente := NodoActual^.Siguiente; NodoActual^.Siguiente := PtrAux; end; procedure EliminaNodo(var Lista: tLista; Nodo: tPtrNodo); var PtrAux: tPtrNodo; begin if (Nodo = PrimerNodo(Lista) ) then

{Se quiere eliminar el primer nodo} Lista := Nodo^.Siguiente else begin PtrAux := AnteriorNodo(Lista,Nodo); PtrAux^.Siguiente := Nodo^.Siguiente; end; dispose(Nodo); end; procedure EliminaLista(var Lista: tLista); begin if ( Lista <> PUNTERO_NULO ) then begin EliminaLista(Lista^.Siguiente); dispose(Lista); Lista := PUNTERO_NULO; end end; end. { Fin de la unidad de biblioteca }

Page 205: Apunte Teorico(AlgoritmosI)(VERSION3)

APÉNDICE A

Pseudocódigo

Page 206: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 206

RESOLUCIÓN DE PROBLEMAS USANDO ALGORITMOS.

PSEUDOCÓDIGO Una situación puede ser descompuesta en cuatro posibles fases o estadíos: 1.- Planteo formal de la situación 2.- La elección de una norma o regla para poder resolver la situación planteada 3.- La implementación de esa norma en un código tal que sea interpretado por el procesador 4.- Ejecución de la implementación a efectos de llegar al resultado buscado. Tomando en consideración esta metodología surgen varios esquemas básicos de trabajo: Esquema 1

Se ejecuta una acción Situación no Se pone a prueba la situación aprobada Situación aprobada De este esquema se desprende el siguiente paradigma conceptual: Repetir Acción Hasta que

Se cumpla la condición

Esquema 2

Page 207: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 207

Se ejecuta una acción

Se pone a prueba la situación Situación Situación no Aprobada Aprobada Acción Uno Acción Dos Lo cual genera el siguiente paradigma: Si condición Entonces Acción Uno Sino Acción Dos Fin Si A efectos de formalizar algunos conceptos básicos con los que trabajaremos, enunciaremos los siguientes axiomas:

& Procesador: es toda entidad capaz de comprender un enunciado y ejecutar el trabajo indicado en el mismo.

& El conjunto de todos los recursos necesarios para la ejecución de un trabajo constituye el ambiente de ese trabajo.

& Una acción es un evento que modifica el ambiente

& Para un procesador dado, una acción es primitiva si su enunciado es suficiente para que pueda ejecutarla sin información adicional.

& Una acción no primitiva debe ser descompuesta en acciones primitivas, para un procesador dado.

Page 208: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 208

& Existen distintas técnicas para descomponer una acción en acciones primitivas. Uno de los métodos, denominado técnica de refinamientos sucesivos o top-down, consiste en: Dado un trabajo T, por medio de un enunciado no primitivo, tal que, transforma el ambiente, desde un estado inicial Ei en un estado final Ef, se puede encontrar una descomposición t1, t2, ... , tn que constituyan una secuencia de enunciados, que ejecutan el trabajo T.

& Una condición es una afirmación lógica sobre el estado del problema que puede ser cierta o falsa, en el momento de la observación.

& Un algoritmo es una secuencia ordenada de acciones primitivas que pueden ser ejecutadas por un procesador y que lleve a la solución de un problema dado.

Como aclaramos al inicio, la resolución de un problema está constituída por cuatro fases. Dos de ellas son: • La construcción de un algoritmo que resuelva el problema dado • La adaptación del algoritmo al procesador. De ahora en más, el procesador es el equivalente a la computadora. La construcción del algoritmo es la etapa más difícil. Las computadoras necesitan codificar los algoritmos para poder ejecutarlos. La codificación se hace mediante lenguajes apropiados, denominados Lenguajes de programación. El primer paso para la resolución de problemas es la formalización de su ambiente. Vamos a definir un conjunto de reglas que nos van a permitir describir con precisión y sin ambigüedad, los objetos del universo del problema. Una primer característica que diferencia entre sí a los objetos es que cada uno tiene un nombre que lo identifica sin ambigüedad, o sea, si queremos citar diferentes abjetos, damos una lista de sus nombres. Cada objeto tiene un uso específico y estos usos no son intercambiables, podemos entonces decir que cada objeto tiene un tipo particular que indica características comunes a todos los estados posibles del objeto. Para describir un tipo, podemos enumerar los diferentes estados que un objeto de ese tipo puede tomar. Ejemplo: Tipo “caja” : {caja de cartón, caja de metal, caja de madera} Esta forma de describir un tipo, no siempre es cómoda. Por ejemplo, los objetos “un número” y el “9” son ambos de tipo numérico, y es evidente que no podemos describir el tipo numérico enumerando todos los números posibles. Describimos el tipo numérico como el conjunto de todos los números que el procesador es capaz de manipular.

Page 209: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 209

Por último, otra característica de los objetos es el valor. En cada instante, todo objeto del ambiente tiene un valor, para algunos objetos, este valor puede cambiar luego de la ejecución de una acción. Es importante recalcar, en relación con el cambio de valor de un objeto, que en tanto no se guarde el valor precedente, éste es imposible de recuperar. Decimos entonces, que cada nuevo valor destruye el anterior. Así como mencionamos arriba que, para algunos objetos, su valor puede cambiar, para otros, su valor nunca cambia. Para resumir, podemos imaginarnos a los objetos de un ambiente, como cajas con tapa en donde aparece adherida una etiqueta (el nombre), además, la caja tiene una cierta forma (el tipo) y contiene una información (el valor). Ejemplo Se tiene un objeto de nombre NUMERO, de tipo numérico, tal que su valor, es un número natural. Se quiere desarrollar un algoritmo que determine el producto de los n primeros números naturales (el factorial de n = n! ). Las acciones primitivas que puede ejecutar el procesador son: • Dar un valor a un objeto • Calcular la suma de dos números • Calcular el producto de dos números El procesador interpreta la condición: • Un número es menor o igual a otro. • También interpreta un esquema de repetición del tipo

Repetir ... hasta que ...

Así planteado el problema, debemos ahora describir con precisión el ambiente con el cual deberá trabajar el procesador. El ambiente consiste del objeto ya descripto de nombre NUMERO. El valor inicial de este objeto está bien determinado y va a servir para control del cálculo. Si n es 4, se calculará: 1 * 2 * 3 * 4 = 24 Si n es 6, se calculará: 1 * 2 * 3 * 4 * 5 * 6 = 720; etc. Dentro del ambiente debemos crear otro objeto cuyo valor final, será el resultado del cálculo: lo llamaremos FACTORIAL y será también de tipo numérico. Los número naturales que intervienen en el producto (1, 2, 3, ..., n) también deben ser definidos dentro del ambiente. Vamos a crear un solo objeto, que llamaremos I, de tipo

Page 210: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 210

numérico y su valor va a variar, de modo tal que, en él estén representados todos los números enteros entre 1 y n. Dijimos que todos los objetos, en cada instante, tienen un valor; FACTORIAL e I, al ser creados tienen un valor no conocido: decimos que tienen valor indeterminado. Para que el objeto tenga un valor determinado es necesario ejecutar una acción. Describamos el ambiente del programa OBJETO

DESCRIPCIÓN

ESTADO INICIAL

ESTADO FINAL

Número Objeto de tipo entero que

representa el número del que se desea hallar el factorial

n n

Factorial Objeto de tipo entero en el cual se calcula el producto de los priemros números enteros.

Valor indeterminado n!

I Objeto de tipo entero que toma todos los valores enteros entre 1 y n.

Valor indeterminado ---

En la tabla anterior no está indicado el valor final de I, ya que no lo conocemos en el momento de componer el algoritmo, pues no sabemos, de antemano, cómo el procesador utilizará ese objeto. Ya descripto el ambiente, estamos en condiciones de componer el algoritmo, es decir, determinar cuáles son las acciones que deberá ejecutar el procesador para transformar el ambiente, del estado inicial, al estado final deseado. Si hacemos que el valor de I, tome sucesivamente los valores enteros entre 1 y n, el primer valor que debe tomar I es 1. Entonces: 1! = 1 2! = 2 * 1 = 2 * 1! 3! = 3 * 2 * 1 = 3 * 2! . . n! = n * (n-1) * ...... * 1 = n * (n-1)! El valor de FACTORIAL en el instante i, se calcula como el valor de I por el valor del objeto FACTORIAL en el instante inmediato anterior. Si en el estado inicial, el valor indeterminado de FACTORIAL es f, en el estado final, su valor será f * n * (n-1) * ... * 1, lo cual es incorrecto, salvo que f, sea 1. Por lo tanto, antes de comenzar a calcular el producto, daremos a FACTORIAL el valor inicial 1. Para cada valor de I se deben ejecutar las siguientes acciones: Mostraremos las distintas etapas en que se puede descomponer este algoritmo, aplicando la técnica de refinamientos sucesivos.

Page 211: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 211

T: calcular el factorial de un número dado T1: dar valores iniciales a los objetos FACTORIAL e I T2: actualizar el valor de FACTORIAL, multiplicando su valor anterior por el valor de I T3: pasar al entero siguiente y repetir T2 hasta que se hayan efectuado todos los productos necesarios Finalmente el algoritmo resultante será: Algoritmo “Factorial de n” T1.1 Dar a Factorial el valor 1 T1.2 Dar a I el valor 1 Repetir

T2 Multiplicar el valor de I por el valor de Factorial y asignar el resultado como el nuevo valor de Factorial

T3.1 Incrementar en 1 el valor de I y asignar el resultado como el nuevo valor de I Hasta que (T3.2) el valor de I sea igual al valor de Número aumentado en 1 Las acciones T1.1; T1.2; T2; T3.1 y T3.2 o bien son primitivas, o bien composición de primitivas. Por ejemplo, la acción: T2: Multiplicar el valor de I por el valor de FACTORIAL y asignar el resultado como el nuevo valor de FACTORIAL está formada por las siguientes acciones primitivas: • Calcular el producto de dos números • Dar un valor a un objeto Transformación del ambiente Al ejecutarse la acción T3.1 del ejemplo anterior:

“ Incrementar el 1 el valor de I y asignar el resultado como el nuevo valor de I” observamos una transformación del ambiente. Se pasa, de un estado del ambiente, en el cual I tiene un valor, a otro estado en el cual I, tiene ese valor, incrementado en 1. Formalicemos, entonces , la definición de acción que dimos anteriormente:

Page 212: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 212

“Descripto el ambiente como un conjunto de objetos, una acción sobre este ambiente es un evento de duración finita que, a partir de un estado inicial, para tal acción, tiene, por consecuencia, un nuevo estado bien definido dentro del ambiente”. En la acción:

“Incrementar en 1 el valor de I y asignar el resultado como el nuevo valor de I”. intervienen dos objetos: I y 1. Durante la ejecución del algoritmo, el valor de I cambia: inicialmente su valor está indeterminado, luego su valor es 1, después 2, etc.. En cambio, el valor del objeto 1 se mantiene inalterable a lo largo de todo el algoritmo. Por lo anterior, clasificaremos a los objetos de la siguiente manera: variables y constantes. El objeto I es una variable y el objeto 1, una constante.

& Una variable es un objeto cuyo valor puede variar y que posee además los siguientes atributos: Un nombre que lo designa Un tipo que designa el uso de la variable

& Una constante es un objeto cuyo valor no puede variar. Programación estructurada En ciertos lenguajes clásicos (como APL o las formas primitivas de FORTRAN y BASIC) se emplea con frecuencia la instrucción GOTO de transferencia incondicional, que permite pasar la ejecución del programa a otra parte del mismo, señalada por una etiqueta. En otros (como C o Pascal) se utiliza un estilo diferente de programar (la programación estructurada) en la que la instrucción GOTO está prohibida o, al menos, desaconsejada. Por ello, la programación estructurada se llama a veces programación sin GOTO. En la programación estructurada se utilizan sólo tres estructuras de control básicas: Ø El bloque de instrucciones consecutivas. Ø La instrucción condicional. En PASCAL existen dos tipos principales:

1. La sentencia SI-ENTONCES (if-then): Si la condición se cumple, se ejecuta la instrucción 1. En caso contrario, se ejecuta la instrucción 2. 2. La sentencia según: - Si la variable tiene el valor1, se ejecuta el bloque de instrucciones 1. - Si tiene el valor2, se ejecuta el bloque de instrucciones 2. Y así sucesivamente. - Si no tiene ninguno de los valores indicados, se ejecuta el bloque de instrucciones n.

Ø El bucle: En PASCAL existen tres tipos principales:

Page 213: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 213

1. La instrucción PARA (for): Se ejecuta primero la instrucción iniciadora. A continuación, mientras la condición se cumpla, se ejecutan repetidamente las instrucciones, seguidas por la instrucción de terminación. 2. La instrucción MIENTRAS (while): Mientras la condición se cumpla, se ejecutan repetidamente las instrucciones. Si la condición no se cumple cuando la ejecución llega a la instrucción while, las instrucciones no se ejecutan ninguna vez. 3. La instrucción REPETIR (Repeat): Mientras la condición se cumpla, se ejecutan repetidamente las instrucciones. Si la condición no se cumple cuando la ejecución llega a la instrucción until el ciclo repetitivo se corta, las instrucciones se ejecutan al menos una vez.

Programación modular La llamada de subrutina (conocida, en general, como instrucción CALL) sirve para relacionar unos programas con otros y permite modular las aplicaciones, descomponiéndolas en dos o más secciones llamadas procedimientos, subrutinas, o funciones, según los casos. Por esta razón los lenguajes de programación clásica se llaman también procedimentales. Gracias a la capacidad de invocar la ejecución de procedimientos, los distintos programas que constituyen una aplicación suelen formar una estructura jerárquica, con un programa principal (main en C) que llama a otros subprogramas, y éstos a otros, hasta llegar a los niveles más bajos de la jerarquía, donde suelen situarse los programas que prestan servicios especiales a casi todos los demás. La jerarquía en cuestión no forma siempre, un árbol invertido, sino un esquema algo más complejo. Es cierto, que siempre suele haber un nodo principal o raíz (el programa principal), pero un mismo nodo puede tener más de un antecesor (puesto que una subrutina puede ser invocada por varios módulos). Además, es posible que haya ciclos, simples o compuestos, pues los módulos pueden ser recursivos, directamente o indirectamente. En cambio, en esta forma de programar, los datos no tienen ninguna organización preestablecida: cada programador decide cómo se relacionan unos con otros y cómo se distribuyen entre los subprogramas. En principio, existen dos clases de datos: · Datos globales: son accesibles por todos los subprogramas. · Datos locales: son accesibles por un solo subprograma, módulo o función. A veces, dependiendo del lenguaje de programación, los datos locales son automáticamente heredables por los subprogramas o funciones situados, en la jerarquía de llamadas, por debajo del subprograma en que dichos datos han sido definidos. Nombres de variables Existen algunas reglas simples que rigen la denominación de las variables, las cuales difieren ligeramente, dependiendo del lenguaje de programación que se utilice. En general se exigirá que la variable tenga un nombre que comience con una letra; después de este primer caracter, los siguientes pueden seleccionarse de un conjunto de caracteres

Page 214: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 214

que contenga letras, dígitos y algunos caracteres especiales. Los espacios no se permitirán dentro de un nombre de variable. Los nombres elegidos deben decir algo sobre el propósito para el cual se emplean las variables. El que describe el algoritmo para la resolución de un problema, es quien elige los nombres de las variables. Nombres bien elegidos, no sólo hacen los algoritmos más fáciles de leer y entender, sino también, de modificar los ya descriptos por otros.

Page 215: Apunte Teorico(AlgoritmosI)(VERSION3)

APÉNDICE B

Diagramas de Flujo

Page 216: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 216

DIAGRAMAS DE FLUJO

Los diagramas de flujo (flowcharts) son esquemas gráficos que representan el flujo de distintas estructuras de control en un lenguaje de programación. Debe quedar claro que, al igual que el pseudocódigo, los diagramas de flujo son independientes del lenguaje de programación que se esté utilizando. Ante todo es preciso definir alguna nomenclatura que se utilizará para construir tales diagramas. Las siguientes son algunas formas básicas, aunque existen muchas más: Entrada de datos Salida de datos

Proceso ó acción sobre los datos Decisión Algunos ejemplos de flowcharts son los siguientes: Acción de secuencia

Page 217: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 217

Acción de selección SI...ENTONCES (IF...THEN) SI NO Acción de selección SEGÚN (CASE OF)

Acción de repetición PARA (FOR)

CONDICIÓN

K

K1 K2 ... Kn

ACCIÓN 1 ACCIÓN 2 . . . ACCIÓN n

Page 218: Apunte Teorico(AlgoritmosI)(VERSION3)

75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 218

Acción de repetición MIENTRAS (WHILE) NO SI Acción de repetición REPETIR (REPEAT) SI NO

CONDICIÓN

CONDICIÓN