Anda di halaman 1dari 218

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

Incluye Manejo de
Punteros y Memoria Lic. Gustavo López
Dinámica,
y Unidades de
Augusto Vega
Biblioteca. Matías Gavinowich
Enrique Calot

Cátedra Lic. Gustavo López


Facultad de Ingeniería – Universidad de Buenos Aires
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

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


CAPÍTULO I

Introducción a Pascal
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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 4
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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 5
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.

Análisis del
problema

Definicón del Datos de Datos de


problema entrada salida o
resultados

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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 6
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.

Módulo principal

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

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

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


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.).
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 8
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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 9
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, ...
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 10
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

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 12
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


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 13
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 14
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;

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


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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 16
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;

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


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

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 19
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

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


Ø 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”

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


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:

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


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.

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


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>;

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


- 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>;

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


CAPÍTULO II

Procedimientos y Funciones
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;

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


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;

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


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 *)
.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 29
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:

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


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

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


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

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


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 * )

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


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

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


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;

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


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;

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


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.

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


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; No es bueno,


porque usa
type MiEntero = integer; variables
globales.
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;

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


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;

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


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;

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


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;

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


type MiEntero = longint;

var base, potencia: MiEntero;

function calcula_potencia(num1, num2: MiEntero): MiEntero;

var resultado, i: MiEntero;

begin
resultado:=1; Falla cuando el
exponente es
for i:=1 to num2 do negativo
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;

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


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);

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


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.

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


CAPÍTULO III

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

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


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);


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).

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


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.

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


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.

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


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:

1 2 3

1 X X

2 X O

3 O

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;

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


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}

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


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.

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


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);

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


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];

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 56
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 57
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.

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


CAPÍTULO IV

Métodos de Búsqueda
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*

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 61
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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 62
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

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


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

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


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.

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


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;

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


CAPÍTULO V

Registros
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;

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 69
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*)
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 70
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);

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


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);

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


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).
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 73
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 74
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 75
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 = ');
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 76
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 77
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 78
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;

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


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]
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 80
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);

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


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;

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


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).


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 83
* 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.

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


CAPÍTULO VI

Archivos
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;

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 87
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)

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


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).

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


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);

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


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’);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 91
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.

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


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

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


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;

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


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

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


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.

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


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;


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 97
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)
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 98
- 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: ‘);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 99
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.}


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 100
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 101
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 102
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)
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 103
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’);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 104
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 105
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);


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 106
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 107
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’);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 108
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 109
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 110
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 111
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 112
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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 113
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 114
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 115
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))
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 116
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:

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


- 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)
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 118
- 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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 119
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+}
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 120
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): ');

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


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.}


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 122
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 123
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.

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


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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 125
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');
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 126
assign(impresora,'PRN');
reset(cuentos);
rewrite(impresora);
imprime(cuentos,impresora);
close(cuentos);
close(impresora);
end.

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


CAPÍTULO VII

Operaciones entre Archivos


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).

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


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.

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


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.

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


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.

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


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.

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


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;

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


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.

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


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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 136
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 137
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 138
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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 139
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.

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


CAPÍTULO VIII

Claves
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

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


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° 24
“H”
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

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


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.

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


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;

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


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): ');
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 146
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 147
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;

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


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;


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 149
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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 150
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.

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


CAPÍTULO IX

Índices
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:

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


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,

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


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:

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


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:

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


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.

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


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.

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


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;

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


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;


75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 160
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;

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


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.

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


CAPÍTULO X

Recursividad
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( ).

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 165
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:

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


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:

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


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
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 168
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.

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


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

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


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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 171
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.

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


CAPÍTULO XI

Manejo de Punteros y Memoria Dinámica


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.

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


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.

HEAP
1003 HOLA, MUNDO.

STACK

PUNT 1003
DATA
SEGMENT

CODE
SEGMENT

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


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);
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 176
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;

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


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).

HEAP

1003 HOLA, MUNDO.

STACK

BAR 1003

FOO 1003

CODE
SEGMENT

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.

1003 HOLA, MUNDO. 1003 HOLA, MUNDO.

1060 PIRULO. 1060 PIRULO.

STACK STACK

foo := bar;

BAR 1003 BAR 1003

FOO 1060 FOO 1003

CODE CODE
SEGMENT SEGMENT

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


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;

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


Analizando el ejemplo podemos graficar lo siguiente:
Lista (tLs)
Prim
Nodo (tLsNodo) Nodo (tLsNodo) Nodo (tLsNodo)
Constante
Elemento Elemento Elemento NIL

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).

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


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;

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


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

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


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;

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


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;

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


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:

553

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


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^);

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


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;

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


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';

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


writeln('La cadena apuntada por PString es = ',
PString^);

{Liberamos la memoria apuntada por PString.}


dispose(PString);

readkey;
end.

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


CAPÍTULO XII

Unidades de Biblioteca
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.

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


Archivo Código Ejecutable
.PAS Compilador Objeto Linkeador

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:

Archivo Código Ejecutable


.PAS Compilador Objeto Linkeador

usa Unid1
usa Unid2

Unid1
(.TPU)

Unid2
(.TPU)

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.

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


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:

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


- 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.

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


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;
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 195
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.

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


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.

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


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.

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


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.

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


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;

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


{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);

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


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

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


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);

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


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 }

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


APÉNDICE A

Pseudocódigo
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

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


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.

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


& 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.
75.40 Algoritmos y Programación I Cátedra Lic. Gustavo López Página 208
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

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


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 n n


representa el número del que
se desea hallar el factorial
Factorial Objeto de tipo entero en el Valor indeterminado n!
cual se calcula el producto de
los priemros números enteros.
I Objeto de tipo entero que toma Valor indeterminado ---
todos los valores enteros entre
1 y n.

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.

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


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:

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


“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:

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


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

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


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.

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


APÉNDICE B

Diagramas de Flujo
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

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


Acción de selección SI...ENTONCES (IF...THEN)

CONDICIÓN
SI NO

Acción de selección SEGÚN (CASE OF)

K1 K2 ... Kn

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

Acción de repetición PARA (FOR)

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


Acción de repetición MIENTRAS (WHILE)

NO
CONDICIÓN

SI

Acción de repetición REPETIR (REPEAT)

SI
CONDICIÓN

NO

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

Anda mungkin juga menyukai