Sie sind auf Seite 1von 176

Programación estructurada en Pascal

ENTREGA NÚMERO 1

MANUAL DE PROGRAMACIÓN BÁSICA

LENGUAJE DE PROGRAMACIÓN
PASCAL

Vladimir Rodríguez 1
Programación estructurada en Pascal

Vladimir Rodríguez 2
Programación estructurada en Pascal

PRIMERA PARTE

Conceptos básicos de programación

Vladimir Rodríguez 3
Programación estructurada en Pascal

Introducción:

Existe un gran número de personas que escriben a foros y blogs en la WEB en la búsqueda de distintos
manuales o tutoriales que enseñen a programar desde cero, o sea, que alguien que no sepa nada en absoluto sobre la
programación sea capas de aprender con solo leer. En efecto, eso es posible y es a lo que apunta este tutorial.
Deben saber que la base para ser un buen programador es la práctica. Deberán trabajar mucho y realizar
muchos programas, por más tontos e inservibles que parezcan, cada vez que aprendan un concepto nuevo.
Encontrarán varios ejercicios en este tutorial que les ayudarán a solidificar lo que hayan aprendido hasta el momento.

Quiero reafirmar la importancia de la práctica. Los lenguajes de programación tienen reglas muy estructuradas
sobre sintaxis y semántica así como también muchas palabras que uno debe ir aprendiendo y comprendiendo en
profundidad. También está el hecho de que uno debe desarrollar una forma de pensar y analizar los problemas
demasiado lógica para lograr resolverlos creando programas de computadora.
Añadiré también que el gran conocimiento de matemática facilitará mucho el desarrollo de software, pero sin
embargo, si no se entienden demasiado con esta materia podrán aprender a programar, pero nunca olviden que la
matemática y la programación están profundamente ligadas.

Una vez hayan leído completamente este tutorial serán capaces de crear aplicaciones básicas en el lenguaje
Pascal, aunque estas podrán tener una gran complejidad. El objetivo de este texto no es que ustedes salgan
programando software para vender ni nada por el estilo, es que aprendan a programar de la nada, es introducirlos al
mundo de la programación estructurada. Teniendo estos conocimientos en mente, ustedes serán capaces de aprender
luego un lenguaje mucho más moderno y complejo con mayor facilidad que si lo intentaran ahora, sin saber nada.

Espero que les sea de mucha utilidad. Ante cualquier duda o aporte que tengan pueden escribirme a mi casilla
de correo: mstrvladi@hotmail.com y les responderé en breve. Desde ya muchas gracias por leer este tutorial.

Vladimir Rodríguez 4
Programación estructurada en Pascal

Nociones sobre Lenguaje de Programación:

Supongo que cualquiera que se aventure a leer este manual es porque sabe lo que es usar una computadora y
por ende, lo que es un programa de computadora, también conocido como aplicación. Sin embargo estoy seguro de
que a la mayoría de las personas que “saben” lo que es un programa, si se les pidiera que le explicaran a alguien que
jamás ha visto una computadora, sin tener una enfrente, lo que es un programa no sabrían como hacerlo. En realidad
es muy sencillo de definir: Un programa es un conjunto de instrucciones bien detalladas que la computadora seguirá
al pie de la letra, nada más. Un programador es quien escribe ese conjunto de instrucciones que serán interpretadas y
seguidas por un procesador.
Dicho así suena muy fácil. Uno escribe instrucciones que serán seguidas al pie de la letra por la computadora.
Sin embargo este es el gran problema que hace que uno como programador tenga que romperse la cabeza pensando
en como hacer que una computadora realice tal o cual acción y como tomar en cuenta todas las variantes que puede
haber acerca de una misma situación.

Ahora bien, si uno como programador ha de escribir instrucciones, ¿cómo lo hace?


Es aquí donde entran en juego los lenguajes de programación. Existen de dos tipos: Lenguajes de bajo nivel
y Lenguajes de alto nivel. Los primeros se escriben en el vejo y querido código binario el cual está constituido
únicamente por 0 y 1 y es lo único que un procesador (CPU) puede entender e interpretar. También se conocen como
lenguaje de máquina. Creo que todo el mundo está de acuerdo en que programar así es una tortura, es inhumano y
sumamente tedioso y aburrido. Y pensar que en principio todo se hacía así y muchas cosas aún se hacen así.
Gracias a la necesidad de poder crear aplicaciones de computadora de una forma más fácil y entendible es que
nacen los Lenguajes de Alto Nivel, que se parecen más a los hablados por los seres humanos y por tanto son mucho
más comprensibles para nosotros. Sin embargo, como dije antes, una CPU solo entiende el lenguaje de máquina y por
ende no uno de alto nivel. Aquí entra en juego un programa muy especial: el compilador. Un compilador es una
aplicación que se encarga de “traducir” ese código de alto nivel parecido a un lenguaje humano a lenguaje de
máquina para que pueda ser interpretado por una Unidad Central de Proceso (CPU). ¿Y entonces como se programa
un compilador? Como ven, a veces hay que seguir luchando con el código binario.
Lenguajes de alto nivel muy nombrados son: C, C++, C-sharp, Pascal, PHP, Java, HTML, Borland, Ruby,
entre muchísimos otros más.
Dado que lo escrito en el lenguaje de alto nivel (código fuente del programa) será luego traducido por un
compilador a 0 y 1, debe hacerse respetando una sintaxis y una semántica bien establecidas, sin excepciones.
Nosotros específicamente en este manual aprenderemos las nociones básicas de la programación estructurada, cómo
se estudia un problema dado y se comienza a crear el programa que lo resuelve, entre muchas otras cosas. Todo lo
haremos con el lenguaje Pascal, que hoy en día solo es usado con fines académicos de aprendizaje, y debo decir que
realmente funciona para ese fin.

Vladimir Rodríguez 5
Programación estructurada en Pascal

Instalando el ambiente de desarrollo Free-Pascal:

Para trabajar con Pascal utilizaremos el compilador Free-Pascal 2.2.2. Este programa debe ser descargado
desde este enlace o desde la página oficial de Free Pascal:
http://sourceforge.net/projects/freepascal/files/Win32/2.2.2/fpc-2.2.2.i386-win32.exe/download.

Una vez descargado el instalador ejecutar el mismo y seguir los siguientes pasos:
Nota: En las imágenes se observa la versión 2.0.2 y no la 2.2.2, sin embargo los pasos de instalación son los
mismos.

Presionar en Next.

Seleccionar un directorio de instalación.


Es recomendable aceptar el directorio que
aparece por defecto (C:\FPC\2.x.x).

Apretar el botón Next.

En la siguiente ventana, se seleccionan los


componentes del compilador que van a ser
instalados. Se recomienda seleccionar Full
Instalation. En caso de que dispongan de
poco espacio en su disco duro, pueden
seleccionar Minimal Instalation

Apretar el botón Next

Vladimir Rodríguez 6
Programación estructurada en Pascal

Next otra vez.

Asociar todos los tipos de archivos que ofrece, sobre


todo los que terminan con la extensión .pas y luego apretar el
botón Next.

Presionar en Install y esperar a que termine de instalar


todos los archivos. Esto a veces puede tardar bastante a pesar
de que el copilador pesa muy poco.

Apretar el botón Next y luego en Finish.

Si llegaron hasta aquí sin errores, el compilador quedó instalado.

Vladimir Rodríguez 7
Programación estructurada en Pascal

Verificando instalación

Ahora debemos verificar si el compilador quedó correctamente instalado.

Lo que haremos es ejecutar el compilador desde la línea de comandos de la siguiente manera:

El primer paso es reiniciar la computadora para que todas las modificaciones realizadas por el programa
instalador tengan efecto. (Es posible que alcance con salir de la sesión y volver a entrar)

Acceder al ítem del menú inicio; Inicio → Ejecutar

Escribir
“cmd” sin
comillas y
apretar la
tecla
ENTER.

En la consola escribir fpc y apretar la


tecla ENTER, como en la figura:

Si todo está bien debería salir un mensaje similar al siguiente:

Para terminar presione enter varias veces hasta que termine de desplegar.

Vladimir Rodríguez 8
Programación estructurada en Pascal

Si en lugar del mensaje anterior, aparece un mensaje que indica que el comando fpc no es válido, significa que
algo anduvo mal en la instalación. Pueden probar a reiniciar la computadora y volver a realizar este paso de
verificación. Si siguen teniendo problemas, les recomiendo desinstalar el compilador y volver a comenzar de nuevo la
instalación.

Si probaron reinstalar y sigue sin funcionar este paso de verificación, será necesario realizar alguna
configuración adicional, como les explico a continuación:

En primer termino, verifiquen la existencia de la carpeta C:\FPC\2.2.2\BIN\I386_WIN32. Pueden


hacerlo utilizando el explorador de Windows, accediendo por Mi PC, luego Disco X: (donde X es la unidad de disco
duro) y así sucesivamente. También pueden verificarlo por el siguiente comando en la consola DOS:

dir C:\FPC\2.2.2\BIN\I386_WIN32

Si esa carpeta no existe, estamos en problemas. La instalación no copió los archivos necesarios, quizás no
tengan los permisos requeridos para la instalación. Intenten volver a instalar.

Si la carpeta existe, verifiquen que en la misma hay un archivo de nombre fpc.exe. Si este archivo existe,
solamente hay que decirle al sistema donde puede encontrarlo (se supone que la instalación lo hace automáticamente,
pero por alguna razón no funcionó). Procedan de la siguiente manera:

1. Accedan Mi Pc → Panel de Control → Sistema


2. Seleccionen la pestaña Avanzado
3. Accionen el botón Variables de entorno
4. En la lista superior pulsen nueva e ingresen estos valores:
o nombre: path
o valor: %path%;C:\FPC\2.2.2\BIN\I386_WIN32
5. Dar aceptar en todas las ventanas.

Insisto que el paso anterior no debería ser necesario, ya que el instalador se encarga de realizarlo, sin embargo
algunos usuarios han reportado ese problema.

Por más información ver: Installing under DOS or Windows en el sitio de Free Pascal.

Corrigiendo un Bug y configurando para comenzar:

Al intentar compilar por primera vez (ya veremos como se hace esto) el compilador indica un error illegal
parameter -Opentium3.
Este es un error que sorprende bastante ya que viene por defecto, pero bueno, es así. Por suerte es bien sencillo de
solucionar.
Al abrir por primera vez el ambiente de desarrollo IDE vallan al menú

Options→Compiler

En el cuadro de texto Aditional Compiler Args, cambiar -Opentium3


por -Oppentium3

Vladimir Rodríguez 9
Programación estructurada en Pascal

Pulsar el botón
OK

Acceder al menú:
Options→Save

Luego cerrar y
volver a abrir el IDE.
Con esto queda
solucionado. En caso de
presentar problemas con
esto envíenme un e-mail
a
mstrvladi@hotmail.com
y les responderé en breve
para ayudarles a solucionarlo.

Configuración de Range Checking e Integer Overflow:

Hace falta añadir una última configuración antes de comenzar a programar, y esta consta de hacer que el
compilador siempre haga un chequeo de que nuestros valores no se van fuera del rango que deben tener. Esto lo
entenderán en más profundidad cuando comencemos a trabajar con arreglos, pero es conveniente tenerlo configurado
desde ahora para evitarse muchos problemas.

Deben ir al menú Options  Compiler y allí presionar sobre la pestaña Generate Code. En el
cuadro Code generation marcarán las opciones Range checking e Integer overflow checking
tras lo cual darán OK.

Ahora sí, ya estamos listos para comenzar a programar. ¡¡¡Adelante!!!

Vladimir Rodríguez 10
Programación estructurada en Pascal

NUESTRO PRIMER PROGRAMA: “¡¡¡HOLA MUNDO!!!”

Al abrir el ambiente de desarrollo IDE vemos una pantalla azul, realmente espantosa, pero es a la que nos
debemos acostumbrar si queremos aprender a programar de verdad. Existen compiladores más bonitos, pero no son
de licencia free y por tanto no los usaremos aquí.
Por defecto, la primera vez que se abre el IDE lo hace con una hoja en blanco cuyo título será
“noname01.pas”. El botón verde en la esquina superior izquierda de la pantalla es para cerrar el archivo actualmente
abierto, luego veremos como trabajar con varios a la vez. La flechita verde en la esquina superior derecha es para
maximizar y minimizar la ventana que contiene dicho archivo. En el borde derecho y también abajo tenemos barras
de desplazamiento para movernos a través del código que escribiremos. Abajo, a la izquierda de la barra de
desplazamiento vemos las coordenadas de nuestra posición: fila:columna, o sea, dónde está nuestro cursor.

Para cada ejemplo de programa escrito aquí analizaremos casi línea por línea lo que significa cada cosa para
que se entienda. En principio uno se dedica a copiar programas hechos sin entender nada y poco a poco va
aprendiendo hasta que se hace capaz de crear los propios. Ahora crearemos un programa muy simple que lo único
que hará es imprimir en la pantalla el clásico texto ¡¡¡Hola mundo!!!:

1 PROGRAM HolaMundo;
2
3 BEGIN
4 write(‘¡¡¡Hola mundo!!!’);
5 END.

Siempre la primera línea de un programa debe comenzar con la palabra reservada PROGRAM (detallaremos
lo que significa que una palabra sea reservada más adelante) seguida por un nombre para el programa
(identificador), en este caso HolaMundo. Luego de eso debe ir un punto y coma (;) para finalizar la primera línea.
Los puntos y comas funcionan como separadores y son los que indican que ha finalizado una sentencia
(instrucción). Siempre luego de una instrucción va un punto y coma para finalizarla.
En la segunda línea no hemos escrito nada, esto es simplemente por una cuestión de legibilidad del código.
En la tercera línea indicamos que comienza el código del programa mediante la palabra reservada BEGIN.
Esto no es una sentencia, por lo cual no debe finalizar con punto y coma, solo indica que a partir de allí estarán las
instrucciones a seguir.
En la cuarta línea indicamos al programa mediante el procedimiento write (que en inglés significa escribir)
que imprima en la pantalla el texto ¡¡¡Hola mundo!!! Siempre luego de un procedimiento write debe
colocarse entre paréntesis lo que queremos escribir en la pantalla, en este caso un texto específico. Siempre que
queramos que un texto aparezca en pantalla tal cual nosotros lo escribimos aquí debemos indicarlo colocando dicho
texto entre comillas simples como se ve en el ejemplo. Como write implica una instrucción, luego de finalizada
colocamos un punto y coma. Notar que dicha instrucción la hemos colocado más hacia la derecha que el resto del
código. Esto no afecta en nada al programa, al igual que en la segunda línea que quedó vacía, sino que lo hace más
legible. A esta acción se la llama Indentación y es muy importante. Luego detallaremos este aspecto al trabajar con
programas complejos y que llevan un código más largo.
La quinta y última línea indica la finalización del programa mediante la palabra reservada END seguida
inmediatamente por un punto (.). Esa es la única palabra que terminará con un punto indicando el fin de la aplicación.

Ahora debemos compilar nuestro


programa. Para ello pueden ir al menú
Compile  Compile o presionar Alt+F9. Se
les pedirá un nombre para el archivo, el cual
no podrá contener espacios ni caracteres
especiales y debe terminar con la extensión
.pas. Luego de eso comenzará
inmediatamente el proceso de compilación. Si
ustedes no han tenido errores de sintaxis, o
sea, si han respetado el lugar adecuado para
cada palabra reservada y no olvidaron ningún
punto y coma o colocaron alguno donde no
iba, aparecerá un cartel con el mensaje
Compile successful: Press any
key.

Esto habrá creado un ejecutable


(archivo de extensión exe, dicha extención
proviene de la palabra “executable” que en
inglés significa ejecutable) en el mismo
directorio en el que han guardado el archivo

Vladimir Rodríguez 11
Programación estructurada en Pascal

.pas. Un archivo pas contiene el código fuente que ustedes han escrito. Por defecto el IDE guarda un archivo en el
directorio X:\fpc\2.2.2\bin\i386-win32, pero ustedes pueden especificar cualquier otro. Es conveniente
que si hacen esto ninguna carpeta de su dirección posea más de ocho caracteres como nombre ni tampoco caracteres
especiales o de espacio.

Ahora pueden abrir su archivo ejecutable haciendo doble clic sobre él. También pueden ejecutar su programa
mediante el menú Run  Run o presionando Ctrl+F9. En este ejemplo específico, como en muchos otros que
veremos, al correr su programa no llegarán ni a leer el texto que aparece ya que se cerrará inmediatamente. Esto no es
ningún error, los programas que escribiremos finalizarán luego de terminar sus instrucciones. Claro que
aprenderemos a crear aplicaciones donde uno puede seleccionar cuando salir.
Si lo ejecutaron desde el IDE simplemente vallan al menú Debug  User screen o presionen
Alt+F5. De este modo verán lo que apareció en la pantalla al ejecutar el programa. Luego presionen cualquier tecla
para volver al IDE. La otra opción es ejecutar su programa desde la línea de comandos: Ir a Inicio  Ejecutar y
escriban cmd para abrir la línea de comandos. En ella deben escribir la dirección en la que está el ejecutable que han
creado y escribir el nombre exacto del mismo, que será idéntico al que ustedes usaron al guardar el archivo .pas de
código fuente. En mi ejemplo yo guardé mi código fuente con el nombre HolaMundo.pas con lo cual habré creado
un ejecutable HolaMundo.exe.
Para todo aquel que no lo sepa, al abrir la línea de comandos aparece un directorio del sistema por defecto.
Para cambiar de directorio en DOS se usa el comando cd (change directory) seguido de la dirección del nuevo
directorio:

Al presionar enter luego de escribir esa instrucción quedaremos posicionados en el directorio donde alojamos
nuestro programa. Obviamente si nosotros creamos otro directorio para trabajar con nuestras aplicaciones pascal
especificaremos la ruta de dicho directorio. Una vez estamos en el directorio correcto escribimos el nombre de
nuestro programa y damos enter. Enseguida se ejecutará y podremos ver sus resultados:

Del mismo modo podemos compilar nuestros programas desde la línea de comandos. Nos posicionamos en el
directorio donde radica nuestro código fuente y escribimos la instrucción fpc HolaMundo.pas para este caso, de lo
contrario la instrucción es fpc NombreDelArchivo.pas donde NombreDelArchivo será el nombre con el cual
guardamos nuestro código fuente.

Vladimir Rodríguez 12
Programación estructurada en Pascal

Write y Writeln:

Ahora veremos un programa que imprimirá dos frases de texto, una en una línea y la otra debajo:

1 PROGRAM SalidaEstandar;
2
3 BEGIN
4 writeLn(‘Hice mi primer programa en Pascal.’);
5 write(‘Logré el clásico “Hola mundo”.’)
6 END.

Al igual que antes en la primera línea escribimos PROGRAM seguido de un identificador para el programa,
finalizando la línea con un punto y coma.
En la línea 3 indicamos el inicio de las instrucciones. En la cuarta línea utilizamos el procedimiento
writeLn para imprimir un texto en pantalla. Este procedimiento funciona exactamente igual que write. La
diferencia entre la una y la otra es que write imprime algo en pantalla y deja el cursor justo al final de esa línea.
De modo contrario, writeln imprime un texto y lo deja al inicio de la línea siguiente. Así, con el ejemplo anterior
obtenemos como salida:

Hice mi primer programa en Pascal.


Logré el clásico “Hola mundo”.

Noten que la última sentencia no finaliza con punto y coma. Esto es porque al final del programa cuando
dictas la última instrucción no hace falta separarla de ninguna otra y es posible omitir el punto y coma, sin embargo
no está mal si lo colocan.
Como ya habrán notado, al ir escribiendo código el IDE irá coloreando las palabras. Aquellas que son
reservadas serán de color blanco (writeln y readln son reservadas pero no se colorean, lo mismo sucederá con otras
tantas), el texto que será impreso tal cual, o sea, el que está entre comillas simples, se ve en celeste, lo demás se ve
amarillo, y así sucederá con otras cosas.

Los procedimientos write y writeln imprimen información en pantalla, en lo que llamamos salida
estándar, o sea, la salida de la línea de comandos. Llamamos salida ya que es información entregada por el
programa. Del mismo modo la llamaremos entrada estándar cuando sea usada para ingresar información al
programa.

Pascal no es un lenguaje que diferencie entre mayúsculas o minúsculas en su código fuente, por lo cual
escribir writeln, WRITELN, WrItElN o como sea, es exactamente igual. Lo mismo sucede con los
identificadores, si ustedes llaman a su programa SALIDAESTANDAR, salidaestandar, SaliDaesTandar o como sea,
da exactamente lo mismo. Esto se aplica para las demás palabras reservadas. Noten que si en un texto que se
imprimirá en pantalla ustedes cambian las mayúsculas y las minúsculas sí se verán las diferencias.

Vladimir Rodríguez 13
Programación estructurada en Pascal

Introducción a las variables:


Muy bien, hasta ahora vimos dos ejemplos de programas que ejecutan una o dos instrucciones para mostrar
información en pantalla. Sin embargo, normalmente hace falta ingresar información a un programa para que con ella,
este realice una o varias tareas. A continuación veremos el ejemplo de un programa que pedirá a un usuario que
ingrese su nombre y luego le saludará nombrándolo:

1 PROGRAM EjemploVariable;
2
3 Var
4 nombre: String;
5
6 BEGIN
7 write(‘Ingresa tu nombre: ‘);
8 readln(nombre);
9 writeln(‘Hola ‘,nombre);
10 END.

Hemos comenzado como siempre escribiendo PROGRAM seguido de un identificador para nuestro programa y
un punto y coma.
En la tercera línea vemos la palabra reservada VAR, la cual indica que a continuación se declararán todas las
variables que usará nuestro programa en sus tareas. Una variable guarda información de algún tipo específico y luego
usa esta para realizar diferentes acciones, incluso modificar su valor. Nosotros podremos modificar el contenido de
una variable siempre que queramos (detallaremos esto más adelante) y en cualquier momento dentro de nuestro
programa.
Para declarar una variable, luego de la palabra VAR, le damos un nombre (identificador) a la variable y
definimos de qué tipo será. La sintaxis para hacerlo es la siguiente:
NombreDeLaVariable: tipo;
en nuestro ejemplo, en la línea 4 hemos declarado una variable llamada nombre del tipo string, o sea, una variable
que contendrá cadenas de caracteres. String es una palabra reservada de pascal y corresponde a un tipo primitivo
del mismo. Todo lo que sea del tipo String almacenará cadenas de caracteres. Nosotros no usaremos mucho este tipo
en general ya que no resulta muy útil a los efectos de este manual, pero aún así sirve conocerlo.
En la línea 6 indicamos el inicio del programa. Como ven, la declaración de variables se hace antes de iniciar
el programa. Esto es porque el compilador primero asigna la memoria RAM que necesitará para almacenar cada
variable y luego comienza con la ejecución del programa. Las variables declaradas se guardan en celdas de memoria
aunque no se usen, por lo cual no conviene declarar variables inservibles.
En la línea 7 usamos write para imprimir un mensaje al usuario y dejar el cursor a continuación del
mensaje, ya que allí se escribirá el nombre. En la octava línea utilizamos el procedimiento readln para leer
información de la entrada estándar e indicamos entre paréntesis que dicha información debe ser almacenada en la
variable nombre (la información se leerá al presionar enter). Siempre luego del procedimiento read o readln se
especifica entre paréntesis la o las variables que guardarán la información leída, de lo contrario, el programa no
guardará ningún registro de la información administrada por el usuario y continuará su curso.
En la novena línea utilizamos el procedimiento writeln al cual le pasamos dos campos de información esta
vez, primero el texto que debe imprimir y luego, separado por una coma, el nombre de la variable cuya información
queremos que aparezca en pantalla. Cuando uno necesita imprimir varias cosas con un solo procedimiento write o
writeln debe separar los campos con una coma. En este caso, un campo contiene el texto y el otro la variable
nombre. Cuando se especifica el nombre de una variable dentro de un campo de estos procedimientos, lo que
aparecerá en pantalla es el contenido de la variable. En este caso la variable nombre guardará la palabra que nosotros
escribamos y luego será lo que aparezca en pantalla al imprimir su contenido.
Ejemplo de ejecución:

Ingresa tu nombre: Vladimir


Hola Vladimir

Al igual que con write y writeln, la diferencia entre read y readln es que la primera lee
información de la entrada estándar y deja el cursor al final de la línea en que la información fue ingresada. La
segunda deja el cursor justo al inicio de la línea siguiente. Pueden probar a escribir el programa anterior
intercambiando estas funciones para ver el resultado y comprenderlo mejor.

Notas: No declarar una variable luego de la palabra VAR es un error de sintaxis.


No usar los dos puntos (:) para luego indicar el tipo de la variable es un error de sintaxis.
Dos variables no pueden llamarse de la misma manera ni tampoco pueden llamarse igual que el programa,
dicho de otra manera, no pueden repetirse dos identificadores, eso es un error de sintaxis.

Vladimir Rodríguez 14
Programación estructurada en Pascal

Ejemplo de asignación: Con el siguiente programa veremos como usar variables numéricas y cómo asignarle
un valor específico a una variable así como también a realizar operaciones entre ellas calculando el área de un
triángulo:

1 PROGRAM AraTriangulo;
2
3 Var
4 base, altura: integer;
5 area: real;
6
7 BEGIN
8 Write(‘Ingresa la base: ‘);
9 Readln(base);
10 Write(‘Ingresa la altura: ‘);
11 Readln(altura);
12
13 area:= base*altura/2;
14
15 Writeln(‘El área del triángulo es: ‘,area:1:2);
16 END.

Este programa utiliza tres variables: base, altura y area, para calcular el área de un triángulo. La
base y la altura del mismo son ingresadas por el usuario, y el valor de area se asigna dentro del programa.
Esta vez tenemos dos variables del tipo integer, o sea que almacenarán números enteros y una del tipo
real, o sea que almacenará números reales. Como ven es posible declarar más de una variable del mismo tipo en
una sola sentencia, simplemente separándolas por comas (,) y asignando el tipo al final, como hemos hecho en la
línea 4. Puede, si se desea, declararse cada variable por separado.
En la línea 8 imprimimos una instrucción para el usuario y en la línea 9 indicamos al programa que lea de la
entrada lo que el usuario ingrese y lo guarde dentro de la variable base. En la línea 10 imprimimos otra instrucción al
usuario y en la 11 leemos un valor y lo guardamos en la variable altura.
En la línea 13 de nuestro programa le asignamos a la variable area mediante el uso del símbolo de asignación
(:=) el valor del resultado de multiplicar el valor de base por el valor de altura y dividirlo entre 2. O sea, estamos
multiplicando ambas variables ya que contienen números. El símbolo para multiplicar es el asterisco (*) y para la
división la barra (/).
En la línea 15 usamos writeln a la cual le pasamos dos campos de información, uno con el texto a
imprimir y otro con la variable cuyo valor debe mostrarse con modificadores de decimales (ya explicaré esto). Este
programa podría haberse hecho sin la variable area y la línea 15 quedaría así
Writeln (‘El área del triángulo es: ‘,base*altura/2:1:2);
Como ven, es posible colocar expresiones en los campos de write y writeln.

Los número enteros no contienen cifras después de la coma y los reales sí pueden contenerlas. La variable
area es del tipo real por lo cual al imprimir su contenido este se mostrará con valores decimales y exponente en base
10. Por ejemplo si su valor es 50, se imprimirá así 5.00000000000000E+001. Esto equivale a 5x10 1. Los
modificadores :1:2 indican en su primer valor el número mínimo de caracteres que toleraremos en el resultado (1) y
el segundo valor indica cuantas cifras luego de la coma queremos que aparezcan (2). Con esto, el valor anterior se
verá como 50.00.

Leer más de una variable a la vez:

El programa anterior podría haber sido escrito de la siguiente manera:

1 PROGRAM AraTriangulo;
2
3 Var
4 base, altura: integer;
5 area: real;
6
7 BEGIN
8 Write(‘Ingresa base y la altura separadas por un espacio: ‘);
9 Readln(base,altura);
10
11 area:= base*altura/2;
12
13 Writeln(‘El área del triángulo es: ‘,area:1:2);
14 END.

Vladimir Rodríguez 15
Programación estructurada en Pascal

Como pueden observar básicamente es lo mismo, con la única diferencia crucial de que esta vez, en la línea 9,
indicamos al programa mediante el procedimiento readln que lea dos variables a la vez. Esto siempre es posible.
Solo deben colocarse entre los paréntesis los nombres de las variables a leer separados por comas. Pueden ser
variables de distinto tipo. En este caso específico ambas son integer. Cuando se ingresen los datos para la entrada
estándar deben separarse por un espacio. Si el usuario ingresa solo un dato éste se asignará a la primera variable que
aparezca dentro de los paréntesis y el programa quedará esperando el siguiente valor.
Del mismo modo pueden imprimirse varias variables a la vez ingresando los nombres de cada variable
separados por comas dentro de los paréntesis de un procedimiento write o writeln.

Ejercicio: Realicen un programa que calcule el área y el perímetro de un rectángulo. Recuerden que el área de
un rectángulo se calcula como base*altura y su perímetro como base*2+altura*2. El programa recibirá de la
entrada estándar la base y la altura y mostrará como salida un mensaje con el área y otro, debajo, con el perímetro.
Estos mensajes deben estar separados al menos por una línea en blanco de los mensajes de instrucciones para el
usuario.
Ejemplo de ejecución:

Ingrese la base: 10
Ingrese la altura: 6

El área de rectángulo es: 60


El perímetro del rectángulo es: 32

Deben ser capaces de realizar este ejercicio antes de continuar. Si desean saber la solución escríbanme a
mstrvladi@hotmail.com .

Vladimir Rodríguez 16
Programación estructurada en Pascal

Introducción a la compatibilidad de tipos:

En el ejemplo anterior tenemos dos variables del tipo integer (enteros) y una del tipo real (reales). Una
variable integer no puede contener reales pero una real sí puede contener enteros. Si en el ejemplo anterior el usuario
ingresa para base un número real no entero (por ejemplo 1.5) el programa intentará guardar este valor en una variable
entera y no podrá por lo cual se producirá un error en tiempo de ejecución que hará que el programa finalice
abruptamente. Si base fuera del tipo real y se le pasara un valor integer (por ejemplo 4) no habría problema ya que el
programa transformará el entero a real, o sea el 4 será 4.0. Esto no afecta la información ya que 4 y 4.0 son
exactamente el mismo valor, pero no hay forma de pasar un real a entero sin modificar este de alguna manera, ya sea
truncándolo (dejar un 1.5 en 1) o redondeándolo de alguna forma.
Si la variable area en nuestro programa fuera también del tipo entero, tendríamos un error en tiempo de
compilación al intentar asignarle el valor de multiplicar base por altura y dividir esto entre dos, ya que la operación
(/) devuelve como valor un real. El compilador detectaría esto y cancelaría su tarea indicando que hay un error en la
compatibilidad de tipos ya que se quiere asignar un real a un entero.
Del mismo modo, si a variables reales o enteras se les asigna una cadena de caracteres como entrada,
tendremos un error en tiempo de ejecución ya que valores numéricos no son compatibles con cadenas de caracteres.

Lo más conveniente para el programa anterior sería declarar todas las variables como reales. Esto soluciona el
problema de la compatibilidad de tipos numéricos, aunque aún tenemos el problema de si el usuario ingresa un
carácter que no sea un número en las entradas de este programa. A la tarea de detectar todas las posibles situaciones
que puede enfrentar nuestro programa al ser usado y sus consecuencias se le llama Captura de errores y es una de
las tareas más difíciles ya que es fácil que se nos pase por alto algún caso, y más aún cuando nuestro programa es
muy complejo. A medida que avancemos veremos cómo enfrentar estas situaciones.

Vladimir Rodríguez 17
Programación estructurada en Pascal

Errores en tiempo de Ejecución y errores en tiempo de Compilación:

Luego de escribir el código siempre intentamos compilarlo para generar nuestro ejecutable y ver los
resultados de nuestro trabajo. Mientras el programa se compila decimos que estamos en tiempo de compilación. Los
errores que pueden sufrirse aquí son generalmente de sintaxis y de compatibilidad de tipos. Cuando hay un error en
tiempo de compilación el compilador se detiene y nos indica el lugar donde encontró el error proporcionándonos
además alguna información acerca de por qué se produjo. Estos son los errores más fáciles de arreglar ya que son
detectables fácilmente.
Ahora bien, que un programa compile perfectamente no nos dice que no tenga errores. Cuando ejecutamos un
programa decimos que estamos en tiempo de ejecución. Los errores en tiempo de ejecución provocan que el
programa se cuelgue, termine inesperadamente perdiéndose toda la información que teníamos o que muestre
resultados inesperados. Estos errores son los menos deseados y los más difíciles de detectar ya que se deben a errores
en nuestras instrucciones al programa.

Veamos un ejemplo sencillo, un programa que divide dos números:


1 PROGRAM dividir;
2
3 Var
4 a, b: real;
5
6 BEGIN
7 Write(‘Ingrese un número: ‘);
8 Readln(a);
9 Write(‘Ingresa otro número: ‘);
10 Readln(b);
11
12 Wtieln(‘El resultado de la división es: ‘,a/b:1:2);
13 END.

Supongamos que sabemos que nuestros usuarios solo ingresarán números a nuestro programa. ¿Cuál sería el
problema entonces? Ese programa compila perfectamente y solo ingresarán números, no habría error posible. Pero
¿qué pasa si cuando se ingresa el valor para b el usuario ingresa el valor 0? Supongamos que la ejecución fue así:
Ingrese un número: 5
Ingrese otro número: 0

¿Qué debería mostrar nuestro programa? Al llegar a la línea 12 he intentar la división de a/b, o sea 5/0, no
podrá ya que la división entre 0 no está definida matemáticamente y el programa producirá un error y se terminará su
ejecución. Lo correcto sería que si el usuario ingresa un 0 para la variable b el programa nos muestre un mensaje de
error y nos vuelva a pedir un nuevo valor. Cuando veamos estructuras de control veremos como tomar distintos
caminos según las condiciones que se dan.

Vladimir Rodríguez 18
Programación estructurada en Pascal

Palabras reservadas:
Las palabras reservadas por Pascal son aquellas que indican alguna función específica del lenguaje, como por ejemplo,
write indica que algo se desplegará en pantalla y readln que el usuario ingresará alguna información. Que una palabra sea
reservada indica que esta no puede utilizarse como identificador, o sea, uno no puede crear un programa llamado Writeln o Var, o
una variable llamada Readln. Utilizar palabras reservadas indebidamente es un error de sintaxis.

He aquí una lista de casi todas las palabras reservadas en pascal:

AND RECORD MAXINT


ARRAY REPEAT NEW
BEGIN SET NIL
CASE THEN ODD
CONST TO ORD
DIV TYPE OUTPUT
DO UNTIL PACK
DOWNTO VAR PAGE
ELSE WHILE PRED
END WITH PUT
FILE ABS READ
FOR ARCTAN READLN
FUNCTION BOOLEAN RESET
GOTO CHAR REWRITE
IF CHR ROUND
IN COS SIN
LABEL DISPOSE SQR
MOD EOF SQRT
NIL EOLN SUCC
NOT EXP TEXT
OF FALSE TRUE
OR GET TRUNC
PACKED INPUT UNPACK
PROCEDURE INTEGER WRITE
PROGRAM LN WRITELN

Vladimir Rodríguez 19
Programación estructurada en Pascal

Identificadores:

Como ya hemos visto, utilizamos identificadores para nombrar a nuestro programa y a nuestras variables. En
efecto, un identificador es un nombre que nosotros asignamos a nuestros objetos en el programa para identificarlos
luego mediante ese nombre. Usaremos identificadores para nombrar variables, constantes, tipos, parámetros,
funciones, procedimientos y punteros entre otras cosas.
Un identificador puede ser cualquier cadena de caracteres alfanuméricos o de subrayado que comience al
menos con una letra. No puede contener espacios ni caracteres especiales así como tampoco puede ser igual a una
palabra reservada.

De este modo, identificadores válidos pueden ser: Abuela, ABuela2,


Ejemplo_de_identificador, E2B214, etc.
Identificadores inválidos podrían ser: 1Abuela, Abuela 2, Ejemplo de Identificador,
Abk$, Variable(), pascal, var, program, etc.

Tipos de datos:

Ordinales: Son aquellos que, dado un elemento, podemos saber cual es el siguiente y/o el anterior además de
que poseen una cantidad finita de elementos. Por ejemplo dado un número entero sabemos que el siguiente es ese
número más uno y el anterior es el número menos uno. Dado el 5 su predecesor es el 4 y su sucesor es el 6.

Integer Números enteros, sin ninguna cifra decimal.


Char Caracteres.
Boolean Valores lógicos, pueden valer True o False.
Enumerados Definido por el programador.
Subrango Definido por el programador.

Para estos tipos llamados Ordinales tenemos tres funciones predefinidas por Pascal que suelen ser útiles:

Pred(valor_de_tipo_ordinal) Devuelve el predecesor para cualquier valor de tipo ordinal excepto si éste es
el primero de todos. En este caso tenemos un error en tiempo de ejecución.

Succ(valor_de_tipo_ordinal) Devuelve el sucesor para cualquier valor de tipo ordinal excepto si éste es el
último de todos. En este caso tenemos un error en tiempo de ejecución.

Ord(valor_de_tipo_ordinal) Devuelve el ordinal para cualquier valor de tipo ordinal. Ya hablaremos de esto
más tarde, al igual que de las dos funciones anteriores.

Reales: Solo tenemos el tipo real para esta categoría. Las funciones anteriores no funcionan con valores de
tipo real ya que es imposible saber, dado un real, cual es su sucesor o su predecesor.

Estructurados: Forman estructuras de datos, en general, bastante complejas.

String Cadena de caracteres.


Array Tablas de datos (String es un array de caracteres).
Record Registros de datos.
File Secuencias de datos (No trabajaremos con este tipo).
Set Conjuntos de datos.

Punteros: Hablaremos de ellos al final de este tutorial.

Notas: El mayor entero posible es identificado por la palabra reservada MAXINT, la cual es una constante
(hablaremos de estas más adelante).

Operadores en pascal:

Veamos los operadores aritméticos de Pascal, los cuales son usados para crear expresiones matemáticas y
realizar cálculos:

Vladimir Rodríguez 20
Programación estructurada en Pascal

Operador Expresión Significado Descripción


A*B A por B Realiza el producto de A
por B. A y B pueden ser
* reales o enteros. Basta con
que uno sea real para que
el resultado sea un real.
A/B A dividido B Realiza la división entre A
/ y B. Devuelve como
resultado un real.
A div B A dividido B (división Realiza la división entera
entera) entre A y B. Devuelve
como resultado el
DIV cociente, por tanto A y B
han de ser enteros.
A mod B Resto de A dividido B Realiza la división entera
(división entera) entre A y B y devuelve
MOD como resultado el resto. A
y B deben ser enteros.
A+B A más B Realiza la suma de A más
B. Basta con que uno de
+ los sumandos sea real para
que devuelva un real.
A-B A menos B Realiza la resta de A
menos B. Basta con que
- uno de los restando sea
real para que devuelva un
real.

Precedencia de operadores: Dada una expresión matemática donde se combinen varios de estos operadores,
al igual que en el álgebra común, se realizarán primero los productos (*), las divisiones (div), (mod) y (/) y luego las
sumas (+) y restas (-). Para quebrar esta precedencia de quién va primero que quién, se utilizan los paréntesis.

Ejemplo: A+B*C En este caso aparece primero la suma y luego un producto. Sin embargo el producto
tiene precedencia ante la suma y se evaluará primero B*C para luego sumar ese resultado con A. Si se quisiera
realizar primero la suma habría que utilizar paréntesis y escribir aquella expresión como (A+B)*C. Ahora sí se
realizará primero la suma y luego el producto ya que los paréntesis tienen la precedencia absoluta excepto si existen
funciones que deben evaluarse dentro.

Funciones matemáticas de pascal:

Pascal nos provee de varias funciones matemáticas predefinidas para realizar algunos cálculos comunes. En
los siguientes ejemplos la letra x simbolizará un número o expresión del tipo adecuado para la función en cuestión:

ABS(x) Calcula el valor absoluto de X. X debe ser integer o real. El resultado es del mismo tipo
que X.
SQR(x) Calcula el cuadrado de X. X debe ser integer o real. El resultado es del mismo tipo que
X.
SQRT(x) Calcula la raíz cuadrada de X siendo X un real o entero mayor o igual que 0. El
resultado es un del tipo real.
SIN(x) Calcula el seno de X expresado en radianes. X debe ser integer o real. El resultado es
del tipo real.
ARCTAN(x) Calcula el arco tangente de X. X debe ser integer o real. El resultado es del tipo real.
EXP(x) Calcula la exponencial de X (ex donde e= 2.7182818…). X debe ser integer o real. El
resultado es del tipo real.
LN(x) Calcula el logaritmo neperiano (base e) de X siendo X mayor que cero. X debe ser
integer o real. El resultado es del tipo real.

TRUNC(x) Suprime la parte decimal de X dejando solo el valor anterior a la coma. X debe ser real.
El resultado es del tipo integer.
ROUND(x) Redondea el valor de X al entero más próximo. X debe ser real. El resultado es del tipo
integer.
ORD(x) Obtiene el número de orden de X dentro de un conjunto de valores definidos por su tipo.
X debe ser de un tipo ordinal. El resultado es de tipo integer.

Vladimir Rodríguez 21
Programación estructurada en Pascal

CHR(x) Devuelve el caracter cuyo número ordinal es X. El tipo de X debe ser integer. El
resultado es de tipo char.

Un ejemplo del uso de DIV y MOD. Introducción a los comentarios:

Se nos plantea el siguiente problema. Dado un número entero de cuatro cifras que será leído desde la entrada
estándar se nos pide que hagamos la suma de sus cifras. Por ejemplo, si el usuario ingresa el número 5648 el
programa debe realizar la suma de 5+6+4+8 y mostrar el resultado. Se asume que el usuario ingresará efectivamente
un número de cuatro cifras.

1 {El siguiente programa desglosará un número de cuatro cifras


2 en sus cuatro números distintos, uno por cada cifra y realizará
3 la suma entre ellas}
4 PROGRAM SumaCifras;
5
6 Var
7 numero: integer; //El valor que será leído.
8 c1, c2, c3, c4: integer;//Una variable para cada cifra.
9
10 BEGIN
11 write('Ingresa un número de cuatro cifras: ');
12 readln(numero); //Leemos el número de 4 cifras.
13
14 (*Asignamos a cada variable el valor de la cifra que le corresponde*)
15 c1:= numero mod 10;
16 c2:= (numero mod 100) div 10;
17 c3:= (numero mod 1000) div 100;
18 c4:= numero div 1000;
19
20 //Mostramos el resultado al usuario.
21 write('El resultado de sumar ',c4,'+',c3,'+',c2,'+',c1,' es: '
,c1+c2+c3+c4);
22 END.

Este es nuestro primer programa en el que vemos el uso de comentarios. Los comentarios son anotaciones
hechas por el propio programador para ir explicando lo que se va realizando en su código. Esto es debido a que
normalmente el software se desarrolla en equipo y resulta imprescindible que otros lean el código que uno escribe y
sean capaces de entenderlo. Los comentarios son omitidos por el compilador por lo cual no afectan en absoluto al
funcionamiento del programa, solo son una documentación explicativa. Existen dos tipos de comentarios, los
comentarios multilínea y los comentarios de fin de línea. Los primeros pueden abrirse con { y cerrarse con } tal como
se ve en las primeras tres líneas de nuestro programa, o abrirse con (* y cerrarse con *) tal como se ve en la línea 14
de nuestro programa. Un comentario multilínea puede ocupar una o más líneas de código, tantas como sea necesario.
Los comentarios de fin de línea se abren con // y no requieren ningún símbolo de cierre ya que, como su nombre lo
indica, finalizan al terminar la línea en que aparecen. Podemos verlos en las líneas 7, 8, 12 y 20 de nuestro ejemplo.
En el IDE se verán de color gris.
Ahora miremos un poco la lógica de nuestro programa, como usando mod y div solamente, puede desglosarse
un número en sus respectivas cifras.
En la línea 12 leemos de la entrada estándar un valor y lo guardamos dentro de numero. Luego, en la línea
15, mediante el símbolo de asignación := le damos a c1 el valor de la primera cifra de nuestro número. Fíjense que
si uno tiene un número de más de una cifra y lo divide entre 10, el resto de esa división será la primera cifra si leemos
de derecha a izquierda. Teniendo por ejemplo el 1456, si dividimos ese número entre 10 (división entera, sin llevarlo
a decimales) obtenemos como resto el 6 y como resultado el 145.
En la línea 13 obtenemos el resto de dividir nuestro número entre 100, eso da como resultado el entero
formado por las dos primeras cifras (de derecha a izquierda) y luego (notar que esta operación está entre paréntesis
para que se realice primero) mediante div obtenemos el cociente de dividir ese número entre 10. Por ejemplo, si
tenemos 1456 y lo dividimos entre 100, el resultado es 14 y el resto 56. Teniendo el resto, al dividirlo entre 10
tenemos como resultado el 5 y como resto el 6. De este modo en las líneas 17 y 18 obtenemos las últimas dos cifras
de nuestro número.
Ahora las variables c1, c2, c3 y c4 contienen cada una los valores de una cifra de nuestro valor inicial.
En la línea número 21 mostramos mediante el procedimiento write los resultados de nuestro programa.

Vladimir Rodríguez 22
Programación estructurada en Pascal

Introducción a las constantes:

Al igual que podemos definirnos variables, que son objetos cuyos valores van cambiando a lo largo del
programa, también podemos definirnos constantes, que serán objetos cuyo valor no cambiará nunca a lo largo del
programa. Veremos un ejemplo en el cual intentaré hacerles entender la necesidad de las constantes.
El siguiente programa pedirá como entrada en reales el precio sin IVA de cinco productos que están a la venta
en un almacén. Luego mostrará el precio de dichos productos con el IVA agregado.

1 PROGRAM preciosIVA;
2
3 Const
4 iva= 23;
5
6 Var
7 harina, leche, azucar, sal, arroz: real;//Precios de los productos.
8 porcentaje: real; //Para calcular cuanto se debe sumar al precio.
9
10 BEGIN
11
12 //Mostramos mensajes al usuario y leemos los datos desde la entrada.
13 write('Precio HARINA : ');
14 readln(harina);
15 write('Precio LECHE : ');
16 readln(leche);
17 write('Precio AZUCAR : ');
18 readln(azucar);
19 write('Precio SAL : ');
20 readln(sal);
21 write('Presto ARRÓZ : ');
22 readln(arroz);
23
24 //Dejamos una línea en blanco.
25 writeln;
26
27 //Calculamos el porcentaje de IVA para harina.
28 porcentaje:= harina*iva/100;
29 //Mostramos el nuevo precio al usuario.
30 writeln('HARINA + IVA : ',harina+porcentaje:2:2);
31
32 //Calculamos el porcentaje de IVA para leche.
33 porcentaje:= leche*iva/100;
34 //Mostramos el nuevo precio al usuario.
35 writeln('LECHE + IVA : ',leche+porcentaje:2:2);
36
37 //Calculamos el porcentaje de IVA para azucar.
38 porcentaje:= azucar*iva/100;
39 //Mostramos el nuevo precio al usuario.
40 writeln('AZUCAR + IVA : ',azucar+porcentaje:2:2);
41
42 //Calculamos el porcentaje de IVA para sal.
43 porcentaje:= sal*iva/100;
44 //Mostramos el nuevo precio al usuario.
45 writeln('SAL + IVA : ',sal+porcentaje:2:2);
46
47 //Calculamos el porcentaje de IVA para arroz.
48 porcentaje:= arroz*iva/100;
49 //Mostramos el nuevo precio al usuario.
50 writeln('ARROZ + IVA : ',arroz+porcentaje:2:2);
51 END.

Bien, en la línea 3 vemos la palabra reservada const, que indica que desde allí se declararán constantes. La
sintaxis para declaración de constantes es
NombreDeLaConstante= valor; donde valor representa un valor de algún tipo primitivo de pascal
(integer, real, char, string entre algún otro). El nombre de la constante es un identificador. En este caso, en la línea 4
del programa hemos declarado la constante iva y le hemos dado el valor 23, que representa el 23% que vale el IVA
en mi país. Notar que las constantes se declaran antes que las variables.

Vladimir Rodríguez 23
Programación estructurada en Pascal

Luego, en las líneas 7 y 8 declaramos las variables del programa, una para el precio de cada producto y una
para calcular el porcentaje de IVA que le corresponde al precio de cada producto para luego sumárselo al mismo.
En las líneas 13 a 22 inclusive, mostramos mensajes al usuario y leemos los valores de las entradas para cada
producto.
Luego, en la línea 25 indicamos mediante el procedimiento writeln que queremos dejar una línea en
blanco. Esto se hace llamando al procedimiento sin pasarle campos entre paréntesis.
En la línea 28 calculamos el porcentaje de IVA para la harina asignando este valor a la variable porcentaje.
Esta operación es simple. Si uno tiene un valor y quiere saber un porcentaje de ese valor simplemente multiplica
dicho valor por el porcentaje que quiere obtener y divide el resultado entre 100. Por ejemplo, si tenemos el número
278 y queremos saber el 15% de ese número simplemente realizamos la operación 278x15/100 lo cual nos daría 41,7.
Ese valor corresponde al 15% de 278. De este modo calculamos el porcentaje de IVA para cada producto
multiplicando su precio por el valor de la constante iva y dividiendo entre 100.
En la línea 30 mostramos un mensaje al usuario y el precio de la harina más el porcentaje de IVA del mismo,
en este caso más un 23%.
Repetimos esto hasta el final de la aplicación.
Notar que siempre usamos la variable porcentaje para calcular el 23% de cada precio. El símbolo de
asignación (:=) borra el contenido anterior de la variable y asigna el nuevo, o sea, la sobrescribe.
Ahora veamos el mismo programa pero sin el uso de la constante iva:

1 PROGRAM preciosIVA;
2
3 Var
4 harina, leche, azucar, sal, arroz: real;//Precios de los productos.
5 porcentaje: real; //Para calcular cuanto se debe sumar al precio.
6
7 BEGIN
8
9 //Mostramos mensajes al usuario y leemos los datos desde la entrada.
10 write('Precio HARINA : ');
11 readln(harina);
12 write('Precio LECHE : ');
13 readln(leche);
14 write('Precio AZUCAR : ');
15 readln(azucar);
16 write('Precio SAL : ');
17 readln(sal);
18 write('Precio ARRÓZ : ');
19 readln(arroz);
20
21 //Dejamos una línea en blanco.
22 writeln;
23
24 //Calculamos el porcentaje de IVA para harina.
25 porcentaje:= harina*23/100;
26 //Mostramos el nuevo precio al usuario.
27 writeln('HARINA + IVA : ',harina+porcentaje:2:2);
28
29 //Calculamos el porcentaje de IVA para leche.
30 porcentaje:= leche*23/100;
31 //Mostramos el nuevo precio al usuario.
32 writeln('LECHE + IVA : ',leche+porcentaje:2:2);
33
34 //Calculamos el porcentaje de IVA para azucar.
35 porcentaje:= azucar*23/100;
36 //Mostramos el nuevo precio al usuario.
37 writeln('AZUCAR + IVA : ',azucar+porcentaje:2:2);
38
39 //Calculamos el porcentaje de IVA para sal.
40 porcentaje:= sal*23/100;
41 //Mostramos el nuevo precio al usuario.
42 writeln('SAL + IVA : ',sal+porcentaje:2:2);
43
44 //Calculamos el porcentaje de IVA para arroz.
45 porcentaje:= arroz*23/100;
46 //Mostramos el nuevo precio al usuario.
47 writeln('ARROZ + IVA : ',arroz+porcentaje:2:2);
48 END.

Vladimir Rodríguez 24
Programación estructurada en Pascal

Esta versión funciona exactamente igual a la anterior, con la diferencia de que en el código, donde antes
teníamos el identificador iva ahora hay un 23.
Ahora imaginen la siguiente situación: el valor de IVA cambia en un momento dado y se les pide que
reestructuren su programa para el nuevo valor de IVA. Si ustedes hicieron su programa tal como esta última versión
deberán buscar en su código cada una de las veces en que usaron el 23 para calcular el porcentaje de IVA y cambiarlo
por el nuevo valor, mientras que en la versión con constante solo es ir a donde está declarada iva y modificar ahí su
valor con lo cual no sería necesario tocar más nada del código. Esta acción puede realizarla hasta alguien que no haya
sido el propio programador, mientras que en el código sin constante, si uno no fue quien programó deberá primero
sentarse a leer y entender el código hasta que pueda efectivamente hacer una búsqueda de los lugares donde se realizó
el cálculo del IVA para poder modificarlos.
En este ejemplo no resulta difícil modificar todos los 23 que hay, pero imaginen una base de datos de un
supermercado en el cual hay miles de productos y precios diferentes. ¿Sería mejor buscar y cambiar todos y cada uno
de los valores en que usamos el 23, o sería mejor declarar una constante y solo cambiar un 23? Normalmente los
códigos son muy extensos y complicados, por lo cual una tarea de revisión total y búsqueda exhaustiva sería muy
tediosa y difícil, además de que es muy probable que se nos pase por alto alguna expresión a modificar. El uso de
constantes es muy recomendable cuando se va a usar un mismo valor muchas veces.

Nota: No puede usarse una constante a la izquierda de una asignación. Por ejemplo sería erróneo escribir
iva:= 10; ya que iva no puede ser modificada. Esto es un error de sintaxis.

Vladimir Rodríguez 25
Programación estructurada en Pascal

Booleanos:

El tipo boolean resulta muy extraño al principio. Solo contiene dos valores posibles: True y False. O sea, una
variable del tipo boolean solo puede ser verdadera o falsa. Aunque en principio uno no puede imaginarse para qué
puede servir una variable de este tipo, realmente resultarán muy útiles más adelante, cuando tengamos que comenzar
a darle condiciones a nuestro programa para que tome distintos caminos de ejecución.
No es posible hacer write ni read de una variable boolean.

Algunas notas:

El punto y coma actúa como separador. Nosotros acostumbramos a escribir una instrucción por línea por una
cuestión de legibilidad del código, sin embargo al compilador eso no le importa al igual que tampoco la sangría
(indentación) que dejemos. De este modo resultará lo mismo escribir

Writeln(‘Hola mundo’);
Readln(variable);

que escribir

writeln(‘Hola mundo’); readln(variable);

Ejercicios: Antes de continuar deberán ser capaces de realizar la gran mayoría de los ejercicios que
aparecerán en los links a continuación. Estos los dirigirán a una página de Facultad de Ingeniería y los ejercicios
están pensados para reforzar todo lo que yo ya les he explicado hasta aquí. La mayoría se resuelve de forma dinámica
con la página WEB con lo cual ustedes podrán verificar sus resultados al instante. Reitero, traten de resolver la mayor
parte de esos ejercicios antes de continuar o de lo contrario no podrán desempeñarse bien en los temas que trataremos
en lo que sigue del tutorial.

Práctico 1: http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Practicos/Practico1
Práctico 2: http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Practicos/Practico2

En caso de tener problemas con estos enlaces o con las páginas a las que estos los llevan envíenme un e-mail a
mstrvladi@hotmail.com y les ayudaré a la brevedad.

Vladimir Rodríguez 26
Programación estructurada en Pascal

SEGUNDA PARTE

Selección y Repetición

Vladimir Rodríguez 27
Programación estructurada en Pascal

Introducción:

En esta sección del tutorial aprenderemos a usar secuencias de selección para indicarle al programa qué
camino ha de tomar en función de ciertas condiciones dadas así como también aprenderemos secuencias de repetición
para indicarle al programa que debe volver a ejecutar cierto fragmento de código una cantidad de veces dada, o no. Es
aquí donde la programación comienza a complicarse un poco y comenzamos a tener verdaderos dolores de cabeza
cuando nuestros programas no hacen lo que nosotros queremos.
Es muy común que, en las secuencias de repetición, nunca se den las condiciones necesarias para finalizar la
repetición y nuestro programa entre en un bucle infinito (loop) y quede colgado realizando la misma tarea hasta que
nosotros lo cerremos o se bloquee el sistema. También es muy común que olvidemos alguna condición necesaria para
que el programa tome un camino específico y por lo tanto funcione erróneamente, o que ni se nos haya cruzado por la
cabeza que el usuario podría haber ingresado tal o cual instrucción y esto genere muchos errores en la ejecución de
nuestro programa.
Por estos motivos es que cuando uno crea un programa debe probarlo con la peor intención posible para
detectar la mayoría de los errores que puedan aparecer. Dedicaré una sección entera para hablar de la prueba de
programas.

El ambiente de desarrollo IDE de Free Pascal tiene incluido un depurador (debugger). Esta es una herramienta
que nos permitirá ver la ejecución de nuestro programa paso a paso y visualizar en una pequeña ventana los valores
que nuestras variables van tomando para así, poder detectar errores que de otro modo sería casi imposible.
Aprenderemos a usar esta herramienta en la página 42 de este tutorial. Conviene leerla cuando lleguen a ella, pero si
necesitan encontrar algún error en los programas que van desarrollando léanla cuando quieran.

Vladimir Rodríguez 28
Programación estructurada en Pascal

Selección. Instrucción IF…THEN e IF…THEN…ELSE:


Como ya hemos visto que se vuelve necesario tomar distintos caminos según lo requieran las circunstancias.
Por ejemplo, en nuestro programa que dividía dos cifras (página 18) hacía falta mostrar un mensaje de error al
usuario en caso de que este ingresara como segundo valor un 0 ya que esto causaría un error en tiempo de ejecución,
y en caso contrario simplemente mostrar el resultado de la división.
Aquí entra en juego la instrucción de selección if…then que en inglés significa (si…entonces). Su sintaxis
es la siguiente:

If condición then
Instrucción;

Supongo que debo decir que IF, THEN y ELSE son palabras reservadas.
Veamos un ejemplo sencillo en el cual un programa pedirá que se ingrese una edad a un usuario y si esta es
mayor a 50 años le dirá que es un viejo.

1 PROGRAM viejo;
2
3 Var
4 edad: integer; //Variable para guardar la edad.
5
6 BEGIN
7 //Mostramos mensaje y leemos la edad.
8 write('Ingrese su edad: ');
9 readln(edad);
10
11 //Mostramos lo mismo que ya ingresó.
12 writeln('Usted tiene ',edad,' años.');
13
14 //Si tiene más de 50 años le decimos viejo.
15 If edad>50 then
16 writeln('Usted es un viejo');
17
18 END.

Lo único nuevo en este programa está en la línea 15, en la instrucción if. Como vemos, en la línea número 9
el usuario ingresa un número entero que corresponderá a su edad. En la línea 15 decimos si edad es mayor a 50
entonces…. La condición edad>50 es un booleano que valdrá true si edad resulta ser mayor a 50 o false en caso
contrario. La línea 16 se ejecutará solo si se cumple la condición, si no será salteada completamente. Es posible
colocar expresiones matemáticas dentro de las condiciones.

Ahora veamos un ejemplo con el mismo programa pero que existan dos caminos posibles a tomar. Si el
usuario tiene más de 50 años le diremos viejo y si no le diremos que es joven:

1 PROGRAM viejo;
2
3 Var
4 edad: integer; //Variable para guardar la edad.
5
6 BEGIN
7 //Mostramos mensaje y leemos la edad.
8 write('Ingrese su edad: ');
9 readln(edad);
10
11 //Mostramos lo mismo que ya ingresó.
12 writeln('Usted tiene ',edad,' años.');
13
14 //Si tiene más de 50 años le decimos viejo.
15 If edad>50 then
16 writeln('Usted es un viejo')
17 else
18 Writeln(‘Usted es joven’);
19
20 END.

Vladimir Rodríguez 29
Programación estructurada en Pascal

Este programa es esencialmente igual al anterior con la excepción del else en la instrucción if. Esto
significa que si se cumple la condición se realizará la primera instrucción (la que está antes del else) y en caso
contrario se ejecutará lo que está luego del else.

Vean que luego de que escribimos if…then la siguiente línea está corrida unas columnas hacia la derecha, o
sea tiene más sangría que todo lo demás. Esto es lo que, como ya dije, llamamos indentación. Es para dejar el código
más legible, de este modo se entiende bien que la instrucción de la línea 16 corresponde al IF y la de la línea 18
corresponde al else.
Notar que la instrucción de la línea 16 no termina en punto y coma. Esto se debe a que si así fuera estaríamos
quebrando el If, o sea, estaríamos diciendo que hasta ahí llega esa instrucción y luego vendrá otra por separado, pero
sin embargo lo que viene es un else que es parte del if y por tanto corresponde a la misma sentencia. En todas las
instrucciones del tipo if…then…else nunca colocar un punto y coma antes del else.

Anidación de la instrucción If…then:

Es posible colocar un if dentro de otro if. A esto se lo conoce como anidación y se usa a muy menudo. Sin
embargo hay que tener mucho cuidado de hacerlo bien o podría darse que nosotros creemos que el programa tomará
cierto camino cuando en realidad no lo hará. Veremos un ejemplo de eso luego del de a continuación:

Veamos un ejemplo en el que se nos pedirá la edad dos veces. Si ingresamos una edad mayor a 50 y otra
menor nos dirá que somos viejos jóvenes.

1 PROGRAM anidacion;
2
3 Var
4 edad1, edad2: integer;
5
6 BEGIN
7 Write(‘Ingresa tu edad: ‘);
8 Readln(edad1);
9 Write(‘Ingresa tu edad nuevamente: ‘);
10 Readln(edad2);
11
12 If edad1>50 then
13 If edad2<50 then
14 Writeln(‘Eres un viejo joven’);
15
16 END.

Como vemos, nuestro programa nos pide el ingreso de números enteros que corresponden a nuestra edad. En
la línea 12 decimos si el valor en edad1 es mayor a 50 entonces si el valor en edad2
es menor a 50 escribe ‘Eres un viejo joven’.

Anidación de un if…then con un if…then…else:

Es posible hacer todas las anidaciones que se quieran, sin embargo como ya dije antes, debemos tener
cuidado. Aquí daré un ejemplo de una anidación que nosotros podríamos interpretar de forma errónea.

1 PROGRAM anidacion2;
2
3 Var
4 edad1, edad2: integer;
5
6 BEGIN
7 Write(‘Ingresa tu edad: ’);
8 Readln(edad1);
9 Write(‘Ingresa nuevamente tu edad: ‘);
10 Readln(edad2);
11
12 If edad1>50 then
13 If edad1=edad2 then
14 Writeln(‘Eres un viejo’)

Vladimir Rodríguez 30
Programación estructurada en Pascal

15 else
16 Writeln(‘Eres joven’);
17
18 END.

En este ejemplo uno podría interpretar que si tenemos más de 50 años y además ingresamos las dos veces la
misma edad aparecerá el mensaje ‘Eres un viejo’, y si edad1 era menor a 50 nos aparecerá el mensaje ‘Eres joven’.
Sin embargo no es así, esa instrucción se fijará si nuestra edad es mayor a 50 y solo si eso es cierto luego verá si
ambas edades eran iguales, si es así aparecerá el mensaje ‘Eres un viejo’ y si no aparecerá el mensaje ‘Eres joven’. La
correcta indentación de esa instrucción es la siguiente:

If edad1>50 then
If edad1=edad2 then
Wirteln(‘Eres un viejo’)
else
Writeln(‘Eres un joven’);

Como ven, solo se ejecutará esto si edad1>50 resulta ser true.


La forma correcta de escribir esta instrucción para obtener el resultado que interpretábamos anteriormente
sería:

If edad1>50 then
begin
If edad1=edad2 then
Wirteln(‘Eres un viejo’);
end
else
Writeln(‘Eres un joven’);

Como ven, hemos encerrado entre begin y end la segunda instrucción if. Esto hace que todo lo que esté
dentro del área abarcada por esas dos palabras sea como una sola instrucción para el programa, a pesar de que allí
dentro podemos poner todas las que queramos. De no hacer esto, el else formaría parte del segundo if y no del
primero. Debemos recordar que un else cierra siempre al if que se encuentre más próximo a él. Para quebrar esto
debemos usar begin y end.
Los delimitantes begin y end se usan para indicar que al tomar cierto flujo de ejecución dada una condición
el programa debe efectuar muchas instrucciones. No serviría de mucho que por cada if que usemos solo podamos
dictar una instrucción. La sintaxis genérica sería la siguiente:

If condicion then
begin
Instruccion1;
Instruccion2;
.
.
.
InstruccionN;
end
else
begin
Instruccion1;
Instruccion2;
.
.
.
InstruccionN;
end;

Vean que también es posible usar los delimitantes begin y end para la instrucción else del if. Estos
begin y end forman lo que llamamos bloques de instrucciones y difieren del begin y end principales ya que
estos forman un único bloque que es el programa completo. Luego de un end va enseguida un punto y coma que
indica el final del bloque y no un punto como en el caso del end principal. Notar que el end anterior al else no
finaliza con punto y coma. Esto se debe a lo que ya expliqué antes. Recordar que nunca, antes de una instrucción
else que forma parte de un if debe ir colocado un punto y coma.

Vladimir Rodríguez 31
Programación estructurada en Pascal

Condiciones compuestas:

Hasta ahora hemos visto como dada una condición específica el programa tomará un camino u otro para
realizar sus tareas. Sin embargo normalmente deben cumplirse varias condiciones para que un programa tome un
camino u otro. Aquí entramos a componer condiciones, o sea, a decir cosas como: Si pasa tal cosa y tal otra has esta
tarea o si pasa tal otra cosa has esta.
Aprenderemos a usar los operadores booleanos para componer condiciones.
Veamos un ejemplo parecido al anterior:

1 PROGRAM anidacion2;
2
3 Var
4 edad1, edad2: integer;
5
6 BEGIN
7 Write(‘Ingresa tu edad: ’);
8 Readln(edad1);
9 Write(‘Ingresa nuevamente tu edad: ‘);
10 Readln(edad2);
11
12 If (edad1>50) and (edad1=edad2) then
13 Writeln(‘Eres un viejo’)
14 else
15 Writeln(‘O eres joven o mentiroso’);
16
17 END.

Aquí vemos que en la instrucción IF hay dos condiciones entre paréntesis unidas por la palabra reservada
AND. Esto significa que se ejecutará la instrucción del IF si ambas condiciones son verdaderas, o sea, si se cumple a
la vez que edad1>50 y que edad1=edad2. Basta con que una de ellas sea falsa para que no se ejecute la
instrucción del IF y se pase a la del ELSE. En ese caso no sabremos si sucedió porque el usuario era joven o si era un
mentiroso que ingresó dos edades diferentes.
Estas composiciones son evaluadas por Free Pascal con el método conocido como circuito corto. Esto es que,
si la primera condición del AND es falsa, ya no se evaluará la segunda porque aunque esta fuera verdadera la
condición total resulta falsa. Así nacen las tablas de verdad. AND en inglés significa Y.
Si nosotros quisiéramos haber dicho que se ejecute el IF si la primera condición ó la otra eran verdaderas
deberíamos haber usado el operador OR. Este es verdadero si al menos una de las condiciones es verdadera. También
se evalúa por circuito corto, o sea, si la primera condición es verdadera ya no se evaluará la segunda porque aunque
resulte falsa la condición total OR será verdadera.

Tablas de verdad:

AND
Expresión1 Expresión2 Expresión1 AND Expresión2
TRUE TRUE TRUE
TRUE FALSE FALSE
FALSE TRUE FALSE
FALSE FALSE FALSE

OR
Expresión1 Expresión2 Expresión1 OR Expresión2
TRUE TRUE TRUE
TRUE FALSE TRUE
FALSE TRUE TRUE
FALSE FALSE FALSE

Existe también el operador booleano NOT que niega una condición, o sea, da el valor opuesto a esta. En
principio uno se pregunta para que serviría algo como eso. Más adelante cuando comencemos a trabajar con la
variables boolean veremos el uso de este operador. Veamos su tabla:
NOT
Expresión NOT Expresión
TURE FALSE
FALSE TRUE

Vladimir Rodríguez 32
Programación estructurada en Pascal

Operadores relacionales: Son los que comparan dos expresiones:

Operador Expresión comparativa Significado


= A=B A es igual a B
> A>B A es mayor que B
< A<B A es menor que B
>= A >= B A es mayor o igual a B
<= A <= B A es menor o igual a B
<> A <> B A es distinto de B

Un ejemplo matemático: Bien. Haremos un programa en el cual aplicaremos casi todo lo dado hasta ahora.
Se nos pide realizar un programa que calcule las raíces de polinomios de segundo grado cuyos coeficientes serán
ingresados por el usuario, y muestre en pantalla los mensajes correspondientes.
Por si alguno de ustedes no está bien familiarizado con polinomios explicaré brevemente lo que es uno de
segundo grado.
Un polinomio es una función matemática con una única incógnita X la cual está elevada a distintos
exponentes en los diferentes términos de la ecuación. Para que se vea mejor, un polinomio de segundo grado tiene la
forma aX2 + bX + c donde a, b y c son números reales. Por ejemplo, un polinomio podría ser 2X2 + 4X + 2. Los
números que acompañan a las X (a, b y c) son llamados coeficientes.
Las raíces de un polinomio son aquellos valores de X que hacen que la ecuación sea nula, o sea, que valga 0.
En el ejemplo que tenemos allí, una raíz de ese polinomio es -1. Si cambiamos la incógnita X por -1 efectivamente
vemos que todo se anula:

2*(-1)2 + 4*(-1) + 2 = 2*1 – 4 + 2 = 2 – 4 + 2= -2 + 2 = 0.

Dado un polinomio de segundo grado puede suceder que tenga dos raíces diferentes, una única raíz (llamada
raíz doble) o que no tenga raíz. Para hallar las raíces de un polinomio aX2 + bX + c se usa la conocida fórmula de
Bascaras:

-b±√(b2-4*a*c)
2*a

El término bajo la raíz cuadrada (b2-4*a*c) es llamado discriminante ya que de él depende si el polinomio
tiene dos raíces, una sola o no tiene. Si el discriminante es mayor que 0 efectivamente tendremos dos raíces distintas
ya que por la forma de la fórmula tenemos un ± que divide la fórmula en dos partes, por un lado resolvemos la
ecuación con el signo de + luego de –b, y luego resolvemos la fórmula con el signo de – luego del –b:

-b+√(b2-4*a*c) -b-√(b2-4*a*c)
2*a 2*a

Esto dará dos resultados diferentes, cada uno correspondiendo a una raíz.
Si el discriminante es nulo, o sea igual a 0, la raíz cuadrada de 0 es igual a 0, por tanto sería estúpido separar
en dos la ecuación ya que hacer –b+0 o –b-0 da exactamente igual, por lo cual el resultado será único. En este caso
tenemos una única raíz doble.
Si el discriminante es menor que 0 no podremos hallar su raíz cuadrada ya que no existe raíz de un número
negativo. En este caso la fórmula no puede resolverse y decimos que no existen raíces reales. En este caso pueden
hallarse raíces complejas, pero eso no lo haremos ahora ya que tiene sentido solo para aquellos que se lleven bien con
las matemáticas.

Muy bien, teniendo cierto conocimiento sobre el problema comencemos a plantearnos como lo resolveremos
para programarlo. El usuario ingresará en la entrada estándar los coeficientes a, b y c de nuestro polinomio. Teniendo
esos valores primero debemos verificar que el valor a sea distinto de 0 ya que de ser así no estaríamos enfrentándonos
a un polinomio de segundo grado sino a uno de grado uno o cero.
Luego deberemos evaluar el discriminante y según lo que este de cómo resultado tomaremos tres caminos
distintos para mostrar los resultados al usuario.

Vladimir Rodríguez 33
Programación estructurada en Pascal

1 PROGRAM polinomio;
2
3 Var
4 a, b, c: real; //Los coeficientes del polinomio.
5 discriminante: real; //El discriminante.
6 raiz1, raiz2: real; //Las raíces del polinomio.
7
8 BEGIN
9 //Pedimos al usuario el ingreso de datos y leemos los mismos.
10 write('Ingrese los coeficientes separados por espacio: ');
11 readln(a,b,c);
12
13 //Verificamos que el coeficiente a sea no nulo.
14 If a=0 then
15 writeln('Usted no ha ingresado un polinomio de segundo grado.')
16 else
17 begin
18 //Calculamos el discriminante.
19 discriminante:= sqr(b)-4*a*c;
20
21 //Tomaremos los tres caminos según el valor discriminante.
22 If discriminante=0 then
23 begin
24 //El polinomio tiene una única raíz.
25 raiz1:= -b/(2*a);
26 writeln('El polinomio tiene una raíz doble: ',raiz1:2:2);
27 end
28 else
29 If discriminante>0 then
30 begin
31 //El polinomio tiene dos raíces.
32 raiz1:=(-b+sqrt(discriminante))/(2*a);
33 raiz2:=(-b-sqrt(discriminante))/(2*a);
34
35 writeln('El polinomio tiene dos raíces reales:
',raiz1:2:2,' y ',raiz2:2:2);
36 end
37 else
38 writeln('No existen raíces reales.');
39 end; //Fin de la instrucción ELSE del IF principal.
40 END.

Muy bien, como primera cosa les diré que a pesar de la explicación que yo pueda dar aquí será conveniente
que ustedes mismos estudien este programa para comprenderlo bien. No es para nada complicado pero puede resultar
confuso gracias a las anidaciones que se han hecho de las funciones IF…THEN…ELSE.
En las líneas 4, 5 y 6 declaramos todas las variables necesarias para que nuestro programa funcione
correctamente.
En la línea número 11 leemos los tres coeficientes a la vez. Luego tenemos un IF…THEN…ELSE principal
que, si el coeficiente a es igual a cero mostrará un mensaje de error al usuario y solo en caso contrario pasará a
realizar todas las demás tareas en su instrucción else. En ese caso calculamos primero el discriminante ya que de él
dependerán los caminos a tomar. Observar que al hacerlo (en la línea 19) usamos la función sqr que calcula el
cuadrado de un número. En vez de eso podríamos haber puesto b*b que es lo mismo.
Si el discriminante es igual a 0 mostraremos al usuario la raíz doble, sino, si el discriminante es mayor que 0
calculamos y mostramos ambas raíces, y en caso contrario le diremos al usuario que no existen raíces reales.
Es importante que entiendan el uso de los begin y end en este programa. Recuerden que estos delimitan
bloques de programa los cuales son vistos como una única instrucción. Si escribiera el código anterior a partir del
primer IF de modo que en cada sentencia tuviéramos una única instrucción quedaría así:

If condicion1 then
Instruccion1
Else
Begin
//Aquí debería estar el cálculo del discriminante.

If condicion2 then
Instruccion2
Else

Vladimir Rodríguez 34
Programación estructurada en Pascal

If condicion3 then
Instruccion3
Else
Instruccion4;
End;

Esto, si tomáramos todo lo que está entre el BEGIN y el END que vemos allí como una única instrucción
quedaría simplemente como

If condicion1 then
Instruccion1
Else
Instruccion2;

donde la Instruccion2 sería todo el enorme bloque entre el BEGIN y END.

Vladimir Rodríguez 35
Programación estructurada en Pascal

Instrucción de selección CASE…ELSE:

La instrucción CASE que veremos a continuación podría considerarse como la forma correcta de escribir una
enorme cantidad de IF anidados. Veamos primero un ejemplo anidando IF en el cual el programa despliega al usuario
varias opciones y dependiendo de lo que éste seleccione será lo que hará:

1 PROGRAM menu;
2
3 Var
4 opcion: char;
5 numero: integer;
6
7 BEGIN
8 writeln('1--> Muestra un mensaje en pantalla');
9 writeln('2--> Ingresar un número');
10 writeln('3--> Ingresar un caracter');
11
12 write('Ingrese una opcion y presione ENTER: ');
13 readln(opcion);
14
15 writeln;
16
17 If opcion='1' then
18 writeln('MENSAJE DESPLEGADO')
19 else
20 if opcion='2' then
21 begin
22 write('Ingrese un número entero: ');
23 readln(numero);
24 writeln('Ha ingresado ',numero,'.');
25 end
26 else
27 if opcion='3' then
29 begin
30 write('Ingrese un caracter: ');
31 readln(opcion);
32 writeln('Ha ingresado ',opcion);
33 end
34 else
35 writeln('ERROR. No existe la opción ',opcion);
36 END.

En este ejemplo trabajaremos por primera vez con una variable del tipo char, o sea que contendrá caracteres,
no una cadena como el tipo string sino uno solo. Esto es por un simple motivo. Al ser del tipo char cualquier símbolo
que ingresemos será leído sin problemas. Nosotros mostramos las opciones del menú con números, entonces uno
podría decir que para leer la opción que el usuario ingrese podríamos haber declarado una variable integer o real, sin
embargo de hacerlo así, si el usuario en vez de ingresar un número ingresa una letra el programa dará un error en
tiempo de ejecución al intentar almacenar una letra en una variable numérica y se cerrará. Sin embargo, al ser del tipo
char, si el usuario ingresa una letra esta se leerá sin problemas y podremos desplegar al usuario un mensaje de error
que indique que ha seleccionado una opción incorrecta, y de ser posible pedirle que lo intente de nuevo.
Notar como en las condiciones de los IF comparamos el carácter leído en la variable opcion con un carácter
escribiendo este entre comillas simples. Es importante saber que si el usuario ingresa un 1 en su entrada, nosotros
leeremos el carácter 1 y NO el número 1. El carácter 1 es como una letra o un símbolo, no es un número que se puede
sumar o restar, es un símbolo. Del mismo modo que uno no puede sumar letras, a+b por ejemplo, no se pueden sumar
caracteres numéricos. No es lo mismo 1 que ‘1’. El primero es un número y el otro un carácter. ‘1’+’2’ equivale a la
cadena ‘12’, y 1+2 es igual a 3. Esta diferencia resulta esencial, es importante que lo comprendan.
No he puesto comentarios en este programa apropósito para que ustedes lo lean y entiendan por sí solos.
Básicamente decimos, si el usuario ingresa el 1 muestra un mensaje, sino, si el
usuario ingresa el 2 pide que ingrese un numero y muéstraselo, sino, si el
usuario ingresa el 3 pídele que ingrese un carácter y muéstraselo, sino dile
que lo que ha ingresado no es una opción correcta.

Vladimir Rodríguez 36
Programación estructurada en Pascal

Ahora veamos el mismo programa usando la instrucción CASE:

1 PROGRAM menu;
2
3 Var
4 opcion: char;
5 numero: integer;
6
7 BEGIN
8 writeln('1--> Muestra un mensaje en pantalla');
9 writeln('2--> Ingresar un número');
10 writeln('3--> Ingresar un caracter');
11
12 write('Ingrese una opcion y presione ENTER: ');
13 readln(opcion);
14
15 writeln;
16
17 Case opcion of
18 '1': writeln('MENSAJE DESPLEGADO');
19 '2': begin
20 write('Ingrese un número entero: ');
21 readln(numero);
22 writeln('Ha ingresado ',numero,'.');
23 end;
24 '3': begin
25 write('Ingrese un caracter: ');
26 readln(opcion);
27 writeln('Ha ingresado ',opcion);
28 end;
29 else
30 writeln('ERROR. No existe la opción ',opcion);
31 end; //De la instrucción CASE.
32 END.

Hasta la línea 16 nuestros ejemplos son exactamente iguales. Ahora, en la línea 17 en vez de tener un IF
tenemos la declaración de la instrucción CASE. Su sintaxis es la siguiente:
Case variable of
Etiqueta1: instruccion1;
Etiqueta2: instruccion2;
...
EtiquetaN: instrucciónN;
Else
Intruccion_por_defecto;
End;

Esta instrucción se fijará en el valor de la variable colocada entre la palabra CASE y la palabra OF, en nuestro
ejemplo se trata de la variable opcion. Luego tomará el camino en el cual el valor de la variable sea igual al de la
etiqueta. El ELSE al final indica lo que se debe hacer si la variable no corresponde con ninguna de las etiquetas
anteriores. Este ELSE puede ir o no. Si no está no se ejecutará nada y se continuará con lo que haya debajo en el
código, pero no habrá error. En este caso hemos usado el ELSE par decir al usuario que no ingresó nada de lo
esperado. Notar que antes de este ELSE sí debe ir un punto y coma que cierre la instrucción anterior, no como en el
caso de los ELSE que se usan en una instrucción IF.
Siempre al final de la instrucción CASE debe ir un END que finalice en punto y coma para indicar el final de
esta instrucción. No colocarlo es un error de sintaxis.
Notar que las instrucciones para cada etiqueta pueden ser bloques de instrucciones delimitados por sus
respectivos BEGIN y END.
Es importante saber que las etiquetas representan los valores que debe tomar la variable del encabezado del
CASE para que el programa tome tal o cual camino. Es posible indicar más de un valor a la vez en una misma
etiqueta, simplemente separando cada valor por una coma:

Case variable of
Valor1, valor2: instruccion1;
Valor3, valor4, valor5: instruccion2;
. . .
ValorI, valorN: instruccionJ;

Vladimir Rodríguez 37
Programación estructurada en Pascal

IMPORTANTE: La variable del encabezado del CASE debe ser de un tipo ORDINAL, o sea integer, char,
enumerado, boolean o subrango.

Ejercicio: Este es un simple ejercicio para que usen CASE. Quiero que en un solo programa junten los
siguientes ejemplos que vimos hasta ahora: el programa HolaMundo de la página 11, el del Área de un Triángulo de
la página 15, el que dividía dos cifras de la página 18 habiéndolo modificado para que muestre un error si se ingresa
la segunda cifra igual a 0, y el programa que desglosaba un número de cuatro cifras de la página 22 modificado para
que muestre un error si el usuario ingresa un número inválido, o sea, uno que sea menor a 1000 o mayor a 9999. Su
programa debe mostrar un menú como el siguiente:

1) Hola Mundo
2) Calcular el Área de un Triángulo
3) Dividir dos números reales
4) Sumar los dígitos de un número de cuatro cifras

Selección:

Cuando el usuario elija una opción su programa habrá de realizar las tareas pertinentes y en caso contrario notificar
el error. No continúen hasta no lograr hacer esto.

Vladimir Rodríguez 38
Programación estructurada en Pascal

Secuencia de repetición FOR:


Además seleccionar distintos caminos dadas ciertas condiciones, muchas veces hace falta repetir ciertas tareas
según otras condiciones. Por ejemplo, en nuestro programa de polinomios de segundo grado sería conveniente que si
el usuario ingresa el coeficiente a nulo, además de mostrar el mensaje de error, el programa vuelva a pedir que se
ingresen los coeficientes, o si en nuestro programa de la instrucción CASE, el usuario ingresara una opción que no
está en el menú además de notificárselo sería correcto volver a pedir que ingrese una.
Existen tres estructuras de repetición; la que vamos a ver a continuación llamada FOR, y otras dos que
veremos luego llamadas WHILE…DO y REPEAT…UNTIL.

FOR (que en inglés significa PARA o POR) repite una tarea una cantidad específica de veces, ni mas ni
menos. Su sintaxis es la siguiente:

For variable:= valor_inicial to valor_final do


Instrucción;

Por ejemplo, si quisiéramos imprimir la letra A unas 10 veces la declaración del FOR sería algo así:

For i:=1 to 10 do
Write(‘A’);

En este caso veríamos una salida como esta: AAAAAAAAAA


La variable en el encabezado del FOR debe ser declara junto con todas las variables del programa. Esta
variable es conocida como variable de control del FOR y debe ser de un tipo ordinal. De más está decir que el valor
final debe ser del mismo tipo que el inicial (por ejemplo puede ser for i:= ‘a’ to ‘z’ do). Su valor es
inicializado con el valor_inicial del encabezado del FOR.
La variable de control comienza valiendo 1, el programa imprime una A, luego la variable aumenta su valor a
2 y se imprime la segunda A. Así hasta que la variable alcanza el valor final. Esto es muy importante ya que es lo más
común usar la variable de control del FOR dentro de las instrucciones que este realiza. Debo añadir que si el FOR ha
de realizar más de una instrucción en su ejecución debemos encerrar estas entre un BEGIN y un END.
Resulta obvio que FOR, TO y DO son palabras reservadas de Pascal.

Veamos un ejemplo en el cual usamos la variable de control del FOR. Imprimiremos en pantalla los 10
primeros número pares a partir del 0 sin incluir a este:
1 PROGRAM pares;
2
3 Var
4 i: integer;
5
6 BEGIN
7 write('Los 10 primeros pares son: ');
8
9 For i:=1 to 20 do
10 If i mod 2= 0 then
11 write(i,' ');
12 END.
En la línea 7 mostramos al usuario un mensaje. En la novena línea comienza la declaración del FOR donde
inicializamos i en 1 y continuaremos hasta que valga 20. El IF en la línea 10 se encarga de ver si el valor actual de i
es par, o sea si el resto de dividirlo entre 2 es igual a 0. En ese caso imprimimos su valor y un espacio, sino no
hacemos nada y esperamos la siguiente iteración.
Al proceso de repetir varias veces un mismo segmento de código se lo llama iteración. Nuestro FOR iterará 20
veces.
Es importante jamás modificar la variable de control mientras se está ejecutando el FOR, o sea, no hacer por
ejemplo i:= 10 dentro del FOR ya que eso produce errores muy inesperados y dependen mucho del compilador
usado para crear el programa. Puede usarse el valor de la variable de control para todo lo que se necesite, incluso
incluirlo en expresiones y condiciones, pero nunca modificarlo.

Ahora veamos el mismo programa, pero solo que esta vez le pediremos al usuario que ingrese cuantos
números pares quiere visualizar:

1 PROGRAM PrimerosPares;
2
3 Var
4 i, pares: integer;
6 BEGIN
7
8 write('¿Cuantos pares quieres visualizar? ');

Vladimir Rodríguez 39
Programación estructurada en Pascal

9 readln(pares);
10
11 writeln;
12 write('Los primeros ',pares,' pares son: ');
13
14 pares:= pares*2;
15
16 For i:=1 to pares do
17 If i mod 2= 0 then
18 write(i,' ');
19 END.

En la línea 8 mostramos el mensaje al usuario para, en la siguiente línea, pedirle que ingrese cuántos pares
desea ver.
En la línea 11 usamos el procedimiento writeln sin parámetros para que imprima una línea en blanco. En la
línea 12 mostramos un mensaje al usuario.
En la línea 14 hay algo nuevo. A la variable pares le asignamos su valor anterior multiplicado por 2 ya que si
el usuario desea ver, por ejemplo, los diez primeros pares nosotros deberíamos iterar del 1 al 20. Esto de asignar a
una variable un valor que depende de su valor anterior es muy común en la programación. A dichas variables se las
conoce como acumuladores. Esto lo veremos en más detalle cuando trabajemos con WHILE y REPEAT.
En la línea 16 cambiamos el valor final del encabezado del FOR por la variable pares. Esto es lo más común
en el uso de esta instrucción. No hace falta indicar un valor nosotros mismos, sino que podemos colocar nombres de
variables, constantes o incluso expresiones matemáticas siempre y cuando el tipo de la variable de control y el del
valor final sean el mismo y estos sean ordinales.

Anidación del FOR:

Veamos un ejemplo donde el usuario ingresará un número que indicará el largo del lado de un cuadrado y
dibujaremos este con el carácter *:

1 PROGRAM Cuadrado;
2
3 Const
4 caracter= '*';
5
6 Var
7 i, j, lado: integer;
8
9 BEGIN
10 write('Ingresa el largo del lado: ');
11 readln(lado);
12
13 writeln;
14
15 For i:=1 to lado do
16 begin
17 For j:=1 to lado do
18 write(caracter);
19
20 writeln;
21 end;
22 END.

Lo interesante en este programa es analizar lo que sucede desde la línea 15 en adelante. Tenemos dos FOR,
uno dentro de otro. Lo primero a resaltar es el uso de dos variables de control diferentes, una para cada FOR. Si
usáramos la misma variable para ambos uno modificaría la del otro y se ocasionaría un caos. Si quieren prueben este
programa así como está y luego con la misma variable de control. Es probable que el compilador los regañe por eso y
se produzca un error en tiempo de compilación.
Veamos, cuando i vale 1 entramos al FOR principal. Allí tenemos otro FOR el cual se ejecutará hasta el final
antes de volver al primer FOR. Cuando j vale 1 imprimimos un * (está definido en la constante caracter). Luego j
vale 2 y se imprime un segundo * en la misma línea ya que estamos usando un procedimiento write. Así
continuamos hasta que se imprimen tantos * en una línea como lo indique la variable largo. Ahora pasamos a la línea
20 del programa donde está writeln para escribir una línea en blanco, o sea, para bajar un renglón. Ahora i vale 2.
Volvemos a entrar en el FOR principal y el FOR de dentro vuelve a comenzar con j en 1. Imprime los *, se baja un
renglón y así hasta que i sea igual al valor de la variable lado.

Vladimir Rodríguez 40
Programación estructurada en Pascal

Notar que la instrucción writeln de la línea 20 está dentro del FOR principal pero no forma parte del FOR
interno.

Otra forma del FOR es la siguiente:

For variable:=valor_inicial downto valor_final do


Instrucción;

Esto funciona exactamente igual a la forma anterior del FOR con la diferencia de que valor_inicial es
mayor que valor_final. O sea que la variable de control irá decreciendo en vez de aumentar. Por ejemplo:

For i:=20 downto 1 do


Instruccion;

En este caso i comenzará valiendo 20, luego 19, luego 18, y así hasta llegar al 1.

NOTAS: Nunca asuman el valor que tendrá la variable de control una vez finalizado el FOR. En este ejemplo
de FOR que va de 20 a 1 uno podría decir que i vale 1 al final, pero esto puede no ser así y depende del compilador.
Si van a volver a usar esta variable deberán inicializarla ustedes mismos con el valor apropiado.

A continuación les planteo una serie de ejercicios de aplicación del FOR y algunos temas ya vistos. Por favor,
no continúen si no son capaces de resolver estos ejercicios. Si realmente se trancan con alguno envíenme un e-mail a
mstrvladi@hotmail.com explicándome cual ejercicio les está dando problemas y les ayudaré o les enviaré la solución.

Ejercicio 1: Determinen cuáles de los siguientes fragmentos de código producen la misma salida:
a) FOR i:=1 TO 3 DO
FOR j:= i+1 TO 3 DO
write(i,j)

b) FOR i:=1 TO 3 DO
write (i, i+1)

c) FOR i:=1 TO 4 DO
IF (i=1) OR (i=4) THEN
write(i)
ELSE
write(i,i)

Ejercicio 2: Escriban un programa que lea desde el teclado un valor entero n. A continuación, el programa
deberá leer n enteros y luego desplegar el mayor y el menor de ellos.

Ejemplo de entrada:

n=8
Ingrese 8 enteros: 5 12 36 4 21 95 12 18

Ejemplo de salida:

El mayor entero ingresado es: 95


El menor entero ingresado es: 4

Vladimir Rodríguez 41
Programación estructurada en Pascal

Ejercicio 3: Escriban un programa que lea de la entrada estándar 5 valores positivos, todos menores que 60 y
produzca una gráfica de barras horizontales similar a la que se muestra en el ejemplo para estos datos:

Ejemplo de entrada:

Ingrese 5 valores: 5 12 17 35 8

Ejemplo de salida:

*****
************
*****************
***********************************
********

Ejercicio 4: Escriban un programa que lea de la entrada estándar un carácter c y un entero n. El programa
debe desplegar un triángulo de n líneas formado por el carácter c según se muestra en el siguiente ejemplo:

Ejemplo de entrada:

n=8
c=$

Ejemplo de salida:

$$$$$$$$
$$$$$$$
$$$$$$
$$$$$
$$$$
$$$
$$
$

Vladimir Rodríguez 42
Programación estructurada en Pascal

DEBUGER

Antes de continuar quiero que aprendan a usar el DEBUGER (depurador) de Free Pascal para que puedan testear sus
programas cuando tengan problemas con ellos.

Primero que nada necesitarán ver la ventana watches que es la que les mostrará los valores que tomen sus variables. Para
acceder a ella vallan al menú Debug  Watches. Deberán disminuir el tamaño de la ventana de su código para poder ver ambas
a la vez. Para ello selecciónenla para que quede activa y presionen Ctrl+F5, el borde quedará verde. Pueden moverla con las flechas
de dirección y cambiar su tamaño manteniendo presionada Shift cuando presionen una flecha. Cuando hayan ajustado el tamaño a
uno que les sirva deben agregar a la ventana WATCHES las variables que necesitan ver. Para ello vallan al menú Debug  Add
watch o presionen Ctrl+F7. En el cuadro de diálogo ingresen el nombre de la variable a observar y esta aparecerá en la ventana
WATCHES. Agreguen tantas variables como quieran.
Ahora deben ejecutar su programa paso a paso. Para ello, una vez compilado, vallan al menú RUN y elijan STEP OVER.
Ahora su programa comenzará pero estará detenido. Presionen F7 o F8 para avanzar una línea. El programa solo avanzará al
presionar una de estas teclas. La diferencia entre una y otra es que F8 no entrará en los procedimientos y funciones (lo veremos
luego) y F7 si lo hará.
Cada vez que avancen verán como se modifican sus variables.
También pueden ir viendo la pantalla de salida mientras usan el debuger, para ello vayan al menú Debug  Output y
les aparecerá la salida. Deberán ajustar el tamaño de cada ventana para que puedan ver todas a la vez.

Vladimir Rodríguez 43
Programación estructurada en Pascal

Secuencia de repetición WHILE…DO:

Hemos visto la repetición FOR que se ejecutaba una cierta cantidad de veces estipulada, ni más ni menos. Sin
embargo, aunque FOR resulta muy útil para muchas tareas, hay cosas que necesitan una repetición que esté
controlada por una o más condiciones.
Por ejemplo, imaginen un programa que pide un código de entrada a un usuario. El usuario tiene 5 intentos
posibles para introducir el código correcto. Si el usuario se equivoca el programa le notifica el error, indica cuantos
intentos le quedan y le vuelve a pedir el código. Si el usuario acierta antes de que se le acaben los intentos el
programa ya no le pedirá el código y continuará su curso. Si el usuario gastó todos sus intentos sin adivinar el
programa se cerrará.
Aquí tenemos una repetición que puede detenerse por dos cosas, que el usuario acierte el código o que gaste
sus intentos. Es imposible saber en qué intento el usuario acertará el código por lo cual una instrucción FOR no
serviría. Veremos este ejemplo utilizando la secuencia WHILE…DO (MIENTRAS…HAS).
Veamos antes que nada su sintaxis:

While condicion do
Instrucción;

Esto significa, mientras se cumpla la condición ejecuta tu instrucción. Una vez hecha la instrucción, while
regresará a su encabezado y volverá a verificar la condición. Si esta sigue siendo verdadera volverá a ejecutarse. En
caso contrario salteará la instrucción y el programa continuará su curso. No está de más decir que la condición puede
ser compuesta y que la instrucción puede ser un bloque de instrucciones delimitado por BEGIN y END.
Veamos el ejemplo del código a adivinar:
1 PROGRAM codigo;
2
3 Const
4 max_intentos= 5;
5 codigo_secreto= 107;
6
7 Var
8 codigo_ingresado, intento: integer;
9 adivino, perdio: boolean;
10
11 BEGIN
12 //Inicializamos los booleanos en false.
13 adivino:= false;
14 perdio:= false;
15
16 intento:=0; //Todavía no ha intentado adivinar.
17
18 While (not adivino) and (not perdio) do
19 begin
20 writeln('Intentos restantes: ',max_intentos-intento);
21
22 intento:= intento+1; //Aumentamos 1 al intento actual.
23
24 write('INGRESE CÓDIGO DE ACCESO: ');
25 readln(codigo_ingresado); //Leemos el código.
26
27 If codigo_ingresado=codigo_secreto then
28 adivino:= true
29 else
30 writeln('ERROR. CÓDIGO INCORRECTO.');
31
32 writeln;
33
34 If (not adivino) and (intento=max_intentos) then
35 perdio:= true;
36
37 end; //Del WHILE.
38
39 If adivino then
40 writeln('Felicidades. Adivinó.')
41 else
42 writeln('No acertó el código en ',max_intentos,' intentos.');
43
44 END.

Vladimir Rodríguez 44
Programación estructurada en Pascal

Definimos para este programa, dos constantes, una llamada max_intentos que contiene la cantidad máxima
de intentos que el usuario tiene para adivinar el código y otra llamada codigo_secreto que contiene el valor numérico
que el usuario debe adivinar. Luego declaramos las variables codigo_ingresado que será la que guardará el valor que
el usuario ingrese y otra llamada intento que dirá la cantidad de veces que el usuario ha intentado adivinar hasta el
momento.
En este ejemplo vemos por primera vez uno de los usos de las variables booblean. En este caso declaré dos,
una llamada adivino que indicará el momento en el cual el usuario adivine el código, y otra llamada perdio que
indicará si el usuario ha gastado todos sus intentos sin adivinar el código.
Comenzamos inicializando ambos booleanos con el valor false ya que el usuario ni ha adivinado ni ha
perdido. También inicializamos la variable intento con el valor 0 ya que el usuario aún no ha comenzado. Es
importante siempre inicializar las variables con los valores apropiados y no asumir que ya lo tienen. Es común que
uno suponga que el valor de intento sea 0 ya que no se le asignó nada hasta el momento, pero esto no es así, depende
mucho del compilador que se use.
Cuando uno declara una variable de algún tipo, esta puede tomar un valor cualquiera al azar que es
desconocido por el usuario (basura). Hay compiladores que las inicializan con algún valor específico. Sin embargo, al
crear un software, nunca sabemos en el futuro cuando deberemos modificarlo o si debemos pasarle nuestro código a
algún otro programador para que él trabaje con este. Si la otra persona usa un compilador diferente entonces el
programa probablemente no funcione como debe.
¡¡¡SIEMPRE INICIALIZAR LAS VARIABLES!!!

En la línea 18 comienza el encabezado del WHILE cuyas instrucciones estarán en un bloque que va desde el
BEGIN de la línea 19 hasta el END de la línea 37.
Veamos el encabezado del WHILE ya que allí las condiciones están dadas por los booleanos. While (not
adivino) and (not perdio) do es equivalente a decir While (adivino= false) and (perdio=
false) do, o sea, estamos diciendo, Mientras adivino es falsa y perdio es falsa has (y allí
comienzan todas las instrucciones). Notar que la condición es una condición compuesta y que será verdadera en tanto
ambas condiciones comprendidas entre el operador AND sean verdaderas, o sea, cada condición es verdadera si los
booleanos son falsos. Esto es confuso, pero analícenlo por un minuto. Notros estamos preguntando ¿adivino es
falsa? Si es así la respuesta es SI, entonces pregunta ¿perdio es falsa? Si esta respuesta también es SI la condición
es verdadera. La condición, no las variables.
Cuando uno está preguntado por el valor de un booleano no hace falta escribir esto como una comparación.
Fíjense que el encabezado del WHILE no está escrito como la segunda alternativa que dí, o sea, si uno quisiera decir
WHILE (variable= true) DO solo debe escribir WHILE (variable) DO. Usar el nombre de un booleano
en una condición ya es preguntar por si es TRUE. Del mismo modo si uno quiere preguntar por si es FALSE, como
en nuestro programa, debe anteponer el operador NOT antes del nombre del booleano.
Bien, nuestro WHILE funcionará en tanto ambos booleanos sean FALSE, basta que uno no lo sea para que la
condición AND sea falsa y por tanto no se ejecute el WHILE. Como nosotros inicializamos ambos booleanos con
FALSE sabemos que la primera vez que el programa llegue al WHILE su condición será verdadera y por tanto se
ejecutará al menos una vez.
Analicemos todo el bloque que compone al WHILE. Lo primero que hacemos es mostrar al usuario un
mensaje que indica sus intentos restantes. Este número es restar el intento actual menos max_intentos.
En la línea 22 aumentamos en 1 el valor de intento ya que el usuario intentará adivinar. Esta es la idea de
acumulador que nombré anteriormente. La instrucción intento:= intento+1 asigna a la variable intento su
valor anterior más uno. Si intento valiera 8 esa instrucción equivaldría a decir intento:= 8+1. Este es el
procedimiento general para usar contadores, por ejemplo un programa que cuente la cantidad de palabras en un texto,
los caracteres, etc.
En las líneas 24 y 25 mostramos al usuario el mensaje que le indica que ingrese un código y luego lo leemos.
Este es ingresado como un entero. Si el usuario ingresa otros símbolos que no sean numéricos nuestro programa se
cerrará abruptamente. Más adelante veremos como recibir una cadena de caracteres numéricos y pasar estos a
números enteros o reales para lograr un verdadero control de errores por parte del usuario. Sin embargo eso no es
pertinente en este momento y asumimos que siempre recibiremos un entero en la entrada estándar.
En la línea 27 usamos un IF para decir que si el código ingresado por el usuario es igual al que debía adivinar,
la variable booleana adivino cambie su valor a TRUE. Si esto sucediera, cuando el WHILE vuelva a su encabezado
para evaluar la condición esta será falsa y ya no se ejecutará. En caso de que el usuario no adivine, el IF pasará a su
instrucción ELSE en donde imprimimos un mensaje al usuario que le notifica de su error. Luego damos una
instrucción de salto de línea.
En la línea 34 tenemos una instrucción IF que en caso de que el usuario no haya adivinado el código y haya
alcanzado el máximo número de intentos cambiamos el valor de la variable perdio a TRUE. Fíjense que el IF tiene
una instrucción compuesta. Tal vez alguno de ustedes se pregunte ¿por qué no simplemente cambias el valor de
perdio a TRUE cuando el usuario haya alcanzado el número máximo de intentos? Entonces yo preguntaría ¿que
pasaría si el usuario adivina justo en el último intento? Aún así se encendería la variable perdio aunque no haya
perdido (llamo encender a que su valor sea TRUE). En ese caso perdio y adivino serían TRUE y eso estaría mal.
Aquí termina el bloque del WHILE y el programa volverá a la línea 18 para evaluar de nuevo la condición de
iteración. Si esta es falsa entonces se salteará todo el bloque y pasará directo a la línea 39, sino volverá a entrar en el
bloque del WHILE.
Ya fuera del WHILE simplemente verificamos con un IF cuál fue el motivo por el que salimos del WHILE y
notificamos al usuario de ello.

Vladimir Rodríguez 45
Programación estructurada en Pascal

El uso que dimos aquí de las variables booleanas es conocido como Banderas Booleanas ya que nuestras
variables se encenderán cuando se cumpla alguna condición y nos avisarán algo. Sería como levantar una bandera
para indicarle algo a alguien. El uso de banderas booleanas es muy común aunque en principio uno no lo crea. A
medida que la práctica en programar es mayor se comprende mejor a los booleanos y su uso aumenta. Yo en
particular hago mucho uso de ellos, llegando incluso a abusarme un poco, cosa que no es aconsejable ya que el
código puede volverse confuso.

Muy bien, ahora que ya conocen el uso de WHILE es hora de darle práctica. Quiero que modifiquen el
programa del triángulo para que cuando el usuario ingrese base o altura con un valor nulo o negativo el programa le
notifique su error y vuelva a pedírselo. Asuman que el usuario siempre ingresa números reales en la entrada, no se
preocupen todavía por si ingresa otros caracteres.
También modifiquen el programa que efectuaba una división entre dos números de modo que si el usuario
ingresa el valor del cociente como 0 el programa se lo indique y vuelva a pedírselo.
Hagan lo mismo con el programa que desglosaba un número de cuatro cifras. Si el usuario ingresa un número
inválido vuelvan a pedírselo hasta que sea válido.

Un pequeño juego: Los tres ejercicios anteriores corresponden a modificar códigos ya hechos por mí y a re-
modificar lo que ya habían hecho en los ejercicios anteriores, que aunque no es del todo fácil es más sencillo que
diseñar un programa desde 0. Ahora les plantearé un pequeño juego para que lo hagan. Podrán usar todo lo que
hemos visto hasta el momento. Este juego se centrará en el uso del WHILE. El problema es el siguiente:

Un jugador deberá adivinar en un máximo de 15 intentos un número entre 1 y 100. Si el jugador adivina, el
programa se lo notificará y terminará su ejecución, en caso contrario el jugador recibirá una pista para volver a
intentar adivinar. El juego le dirá si el número que debe adivinar es mayor o menor que el que ha ingresado y el
jugador volverá intentarlo.
En cada caso el juego deberá mostrar al jugador el número de intento actual y cuantos restantes le quedan.
Cuando el usuario pierda, el programa deberá mostrar cuál era el número que debía adivinar. Siempre
asumiremos que recibiremos un entero desde la entrada.
El número a ser adivinado será generado al azar por el juego (ya explicaré esto a continuación).

Ejemplos de ejecución:

Ejemplo 1:
Dispones de 15 intentos para adivinar.
1)--> 98
¡¡¡Muy bien!!! ¡¡¡Has adivinado!!!

Ejemplo 2:
Dispones de 15 intentos para adivinar.
1)--> 99
Lo siento, no has acertado.
El número que debes adivinar es menor.

Dispones de 14 intentos para adivinar.


2)--> 80
Lo siento, no has acertado.
El número que debes adivinar es mayor.

Dispones de 13 intentos para adivinar.


3)--> 85
¡¡¡Muy bien!!! ¡¡¡Has adivinado!!!

Ejemplo 3:
Dispones de 15 intentos para adivinar.
1)--> 60
Lo siento, no has acertado.
El número que debes adivinar es menor.

. . .

Dispones de 1 intentos para adivinar.


15)--> 13
Lo siento, no has acertado.
Lamentablemente has perdido. El número era 10.

Vladimir Rodríguez 46
Programación estructurada en Pascal

Generar números aleatorios: Para obtener un número al azar se utiliza la función random. Su sintaxis es la
siguiente:

Random(x) donde X es un entero que marca el número máximo que se puede generar. Random(x)
devolverá un número aleatorio entre 0 y X-1. Por ejemplo, si escribimos random(50) obtendremos un número al
azar entre 0 y 49 inclusive.

Veamos un ejemplo sencillo en el que el usuario ingresa cuál es el tope y el programa le devolverá un número
aleatorio entre 1 y el número ingresado. Asumimos que el usuario ingresará un entero mayor que 1:

1 PROGRAM funcion_random;
2
3 Var
4 x, valor_aleatorio: integer;
5
6 BEGIN
7 randomize;
8
9 write('Ingrese un valor: ');
10 readln(x);
11
12 valor_aleatorio:= random(x) + 1;

14 writeln('El número obtenido entre 1 y ',x,' es:


',valor_aleatorio);
15 END.

Bien, este programa es bien sencillo de entender. Tenemos dos variables, x y valor_aleatorio. La
primera será leída desde la entrada y guardará el valor ingresado por el usuario. La segunda guardará el número al
azar obtenido por la función random.
Lo primero a notar es que en la línea 7 hacemos una llamada al procedimiento randomize. Este no lleva
parámetro alguno, simplemente indica que luego en algún momento del programa será utilizada la función random.
Si no hacen esto no podrán usar esta función y tendrán un error en tiempo de compilación.
En la línea 10 obtenemos el valor de X. En la línea 12 asignamos a valor_aleatorio el valor obtenido
por random(x) más 1. ¿Por qué más 1? Si random(x) genera un número aleatorio entre 0 y X-1 pero nosotros
queremos uno entre 1 y X solo basta sumar uno al resultado. Por ejemplo, si hiciéramos random(10) podríamos
obtener los números 0, 1, 2, 3, 4, 5, 6, 7, 8 o 9. Si quisiéramos un número entre 1 y 10 basta sumar uno al valor
sorteado. Si sale el 0, al sumar 1 obtenemos el 1. Si sale 9, al sumar 1 obtenemos el 10, o sea, corremos todo el
intervalo un lugar a la derecha.

Ahora sí, ya deberían ser capaces de crear el juego del Adivinador.

Toda duda que les surja deben enviármela a mstrvladi@hotmail.com .

NOTAS: En el ejemplo que trabajamos, nosotros usamos un IF para decidir si cambiábamos el valor de un
booleano a TRUE. Sin embargo no es necesario hacerlo así, la siguiente instrucción If
codigo_ingresado=codigo_secreto then
adivino:= true

equivale a escribir adivino:= codigo_ingresado=codigo_secreto. De este modo estamos diciendo que


adivino es true si se cumple la condición codigo_ingresado=codigo_secreto. Si esto no es así el booleano
será FALSE. Esto es exactamente igual que la instrucción con IF.

Vladimir Rodríguez 47
Programación estructurada en Pascal

Secuencia de repetición REPEAT…UNTIL:

Esta secuencia funciona de forma muy similar al WHILE, tanto así que algunos se preguntan cual es su
verdadero propósito. Sin embargo a veces resultará útil utilizar REPEAT y otras veces será mejor utilizar WHILE. La
sintaxis de esta instrucción es la siguiente:

REPEAT
Instrucciones;
UNTIL condicion;

Como primera diferencia notoria entre el WHILE y el REPEAT es que el primero verifica sus condiciones al
inicio y el segundo lo hace al final. La instrucción REPEAT realizará todas las instrucciones comprendidas entre la
palabra REPEAT y la palabra UNTIL, no hace falta colocar BEGIN y END para delimitar el bloque.
Si tradujéramos al español, esta función significa REPITE…HASTA QUE… a diferencia del WHILE que
repetía mientras que una condición fuera verdadera, REPEAT lo hace hasta que esa condición sea verdadera, o sea,
sigue iterando mientras su condición es falsa, no como WHILE que lo hacía mientras era verdadera.

Como diferencia esencial, sabemos que REPEAT se ejecutará siempre al menos una vez ya que verificará su
condición de iteración al final, no como WHILE que podría no ejecutarse nunca. Esto es lo que nos dice cuando
conviene usar uno u otro. Si sabemos que nuestro bloque de código siempre se ejecutará al menos una vez es
conveniente usar un REPEAT en vez de un WHILE, sin embargo no es incorrecto hacerlo aunque sí aconsejable.

Centinela: Veremos ahora, aunque sin usar REPEAT un ejemplo de iteración por centinela. Un centinela es
un valor que tomará nuestra variable para indicar el fin de la iteración, algo así como una bandera booleana, pero
aplicado a otras variables. Veámoslo para comprenderlo mejor. El siguiente programa irá leyendo de la entrada
estándar una serie de números enteros positivos hasta que se ingrese un negativo. En ese caso ya no se leerán más
números y luego se mostrará la suma de todos los números ingresados (excepto el negativo):

1 PROGRAM centinela;
2
3 Var
4 numero, suma : integer;
5
6 BEGIN
7 (* lectura inicial *)
8 ReadLn(numero);
9
10 (* inicialización *)
11 suma:= 0;
12
13 while numero > 0 do
14 begin
15 (* acumulación *)
16 suma:= suma + numero;
17 (* siguiente lectura *)
18 ReadLn(numero);
19 end;
20
21 (* mostrar resultado *)
22 WriteLn('La suma de los números es: ', suma);
23 END.

Bien, en la línea 8 leemos el primer valor que debemos sumar. En la línea 10 inicializamos la variable suma
con el valor 0 ya que aún no ha sumado nada.
En la línea 13 comienza el WHILE solo si el número que ingresamos es mayor que 0, en caso contrario no se
ejecutará. Aquí vemos por qué es mejor utilizar un WHILE que un REPEAT, ya que si el usuario ingresa como
primer valor un número negativo ya no habrá nada para sumar.
En caso de entrar en el WHILE aumentamos el valor de la variable suma sumando su valor anterior al valor
del número leído, o sea, al valor de numero. En la línea 18 volvemos a leer un número y el WHILE volverá a su
encabezado. Si el nuevo número leído es mayor que 0 ya no habrá iteración y en caso contrario sí.
Luego de haber salido del WHILE mostramos al usuario el resultado de la suma.

Bien, esto era para mostrar la idea de centinela. En este caso nuestro centinela es cualquier número negativo
ingresado por el usuario ya que éste será el que detenga la iteración del WHILE. Veremos muchos más ejemplos de
centinelas de aquí en adelante.

Vladimir Rodríguez 48
Programación estructurada en Pascal

Ejercicio: Muy bien, ahora quiero que modifiquen el programa de la página 44 cambiando la instrucción
WHILE por un REPEAT.

Un ejemplo más complejo. Contador de palabras:

Ahora veremos un ejemplo de programa que cuente la cantidad de palabras en una línea de texto que será
ingresada por el usuario. Dicho texto debe terminar con un espacio y un punto sí o sí (centinelas):

1 PROGRAM cuenta_palabras;
2
3 Const
4 ESPACIO= ' ';
5 FIN= '.';
6
7 Var
8 caracter: char; //Caracter leído.
9 cantidadPalabras: integer;//Cantidad de palabras contadas.
10
11 BEGIN
12 //Salteamos todos los espacios al inicio del texto.
13 Repeat
14 read(caracter)
15 Until caracter <> ESPACIO;
16
17 //En cada iteración consumiremos una palabra.
18 While caracter <> FIN do
19 begin
20 //Leemos caracteres hasta encontrar un espacio.
21 Repeat
22 read(caracter);
23 Until caracter = ESPACIO;
24
25 //Aumentamos en 1 el contador.
26 cantidadPalabras:= cantidadPalabras + 1;
27
28 //Salteamos los espacios que separan las palabras.
29 Repeat
30 read(caracter)
31 Until caracter <> ESPACIO;
32 end; //Del While.
33
34
35 writeln('La cantidad de palabras en el texto es:
',cantidadPalabras);
36 END.

Les dejaré como tarea que estudien este código hasta que lo entiendan completamente. Fíjense como
implementamos distintos REAPEAT dentro de un WHILE y como usamos el procedimiento READ para siempre ir
leyendo sobre la misma línea. No olviden las constantes declaradas al inicio del programa. Los comentarios son su
gran guía y eso es algo muy importante para un programador. Si deben trabajar con el código de su compañero de
trabajo deberán ser capaces de entender sus códigos basados en que saben la tarea que desempeña su programa, las
variables que declaró tienen nombres mnemotécnicos y sus comentarios indican bien cada acción aunque sin
extenderse o abusar de ellos. Todo esto es realmente muy importante que aprendan a implementarlo.
Por si alguno de ustedes no lo sabe, un nombre mnemotécnico es aquel que ayuda a saber el propósito de una
variable, por ejemplo, en este programa la variable que guarda el valor de cuantas palabras se cuentan se llama
cantidadPalabras. Bien podía haberse llamado cont que es una abreviación de contador o podría haber tenido
cualquier otro nombre. Usen nombres mnemotécnicos aunque sean largos, esto ayuda a prescindir un poco de los
comentarios y a que el código sea más entendible

Ejemplo de captura de error:

En el siguiente ejemplo veremos un programa simple que le pedirá al usuario que ingrese un valor entero. En
esta ocasión veremos como leer estos valores como caracteres y luego si son correctos pasarlos a enteros tal como
debería ser. Primero veamos algo que debería haber nombrado ya hace bastante pero sin embargo decidí aplazarlo
hasta ahora.

Vladimir Rodríguez 49
Programación estructurada en Pascal

En la 20 nombré por ahí algo así como el ordinal de un elemento. Los tipos ordinales de Pascal son aquellos
que están acotados, o sea, que tienen un inicio y un final. Tal es el caso del tipo integer, que está acotado
inferiormente por el valor -32768 y superiormente por MAXINT que vale 32767. Ninguna variable del tipo integer
puede exceder estos valores. Si han activado el range checking tal como se los indiqué al inicio del tutorial
deberían recibir un error en tiempo de compilación al intentar guardar en una variable entera algún valor fuera del
intervalo [-32768, 32767], de lo contrario el error lo obtendrán en tiempo de ejecución.
Tenemos el tipo char que contiene todos los caracteres existentes. Obviamente este tipo es acotado ya que
tiene una cantidad finita de elementos.
El tipo real no es acotado ya que posee una cantidad infinita de elementos. Nunca se puede determinar el
siguiente a un real. ¿Cuál número es el siguiente a 1.5? ¿Tal vez el 1.6? No porque antes está el 1.55, pero antes de
ese está el 1.51, pero antes está el 1.501, y así podríamos continuar infinitamente. Siempre entre dos números reales
cualesquiera hay infinitos reales. Esto en matemáticas es llamado densidad de los reales.

Lo importante aquí es hablar del ordinal de un carácter. Los caracteres están todos incluidos, para Free Pascal
(existen otras tablas), en una tabla llamada Tabla Ascii. Esta tabla le asigna a cada carácter un valor numérico según
su posición. Para ver la tabla Ascii en el IDE simplemente vayan al menú Tool  Ascii table.

En esa imagen el cursor está posicionado sobre el símbolo @. Como vemos, a este carácter le corresponde el
valor 64. Ese valor es el ordinal de @. Si nosotros tenemos un carácter cualquiera y queremos saber su ordinal
debemos usar la función ord. Por ejemplo si escribiéramos ord(@) obtendríamos el entero 64. A la inversa tenemos
la función chr que dado un entero nos devuelve su correspondiente carácter en la tabla. Si escribiéramos chr(64)
obtendríamos como valor devuelto el carácter @.
La función ORD transforma un carácter en entero, o sea, de un tipo char obtenemos un tipo integer. Esto es lo
que usaremos para, dado un carácter numérico, obtener el entero de ese carácter, o sea, dado el carácter ‘1’ obtener el
número 1.
Sin embargo no es tan fácil, observen ustedes en la tabla que al carácter ‘0’ le corresponde el entero 48 ya que
está en la posición 49 de la tabla (la primera posición es la 00). Al carácter ‘1’ le corresponde el entero 49 y así
sucesivamente. Por lo tanto si nosotros leyéramos un carácter de la entrada estándar, suponiendo que el usuario
ingresó el ‘1’, al hacer ORD(‘1’) obtendríamos el número 49 y no el número 1.

¿A alguno de ustedes se les ocurre como, dado un carácter numérico, obtener su valor en entero? O sea, si me
dan el ‘2’ obtener el 2, si me dan el ‘8’ obtener el 8, y así.
Como pueden observar, los lugares de la tabla están ordenados ascendentemente, por lo cual si al ‘0’ le
corresponde el 48, al ‘1’ le corresponde el 49, al ‘2’ el 50 y así hasta el ‘9’ que le corresponde el 57. ¿Entonces que
pasa si al ordinal de cualquier carácter numérico le resto el ordinal de ‘0’? Por ejemplo, si me dan el ‘0’, resto ese
ordinal con el ordinal de ‘0’:
Ord(‘0’)-Ord(‘0’)= 48 – 48= 0. Obtuve el entero 0. Ahora si me dieran el carácter ‘1’ y a su
ordinal le resto el ordinal de ‘0’:
Ord(‘1’)-Ord(‘0’)= 49 – 48= 1. Obtuve el entero 1. De tener el ‘9’ y restarle su ordinal al ordinal
de 0 tendría:
Ord(‘9’)-Ord(‘0’)= 57 – 48= 9. Obtuve el entero 9.

NOTA: El ordinal de un entero es el mismo entero: Ord(57)= 57.


El ordinal de un boolean es 1 para TRUE y 0 para FALSE.

Vladimir Rodríguez 50
Programación estructurada en Pascal

Vayamos al ejemplo entonces. Le pediremos al usuario que ingrese un entero, pero lo leeremos como caracter.
Si los caracteres leídos difieren de los numéricos le diremos al usuario que hay error, sino, transformaremos los
caracteres a enteros. Para este programa usaremos la función EOLN que es TRUE cuando el cursor de la entrada
estándar está en el fin de la línea y FALSE en caso contrario.

1 PROGRAM enteros;
2
3 Var
4 car: char; //Caracter leído.
5 entero: integer; //Guardará el valor en entero.
6 error: boolean; //TRUE si el usuario no ingresa un entero.
7
8
9 BEGIN
10 primera_vez:= true;
11
12 //Mostramos un mensaje al usuario y leemos la entrada.
13 write('Ingrese un entero: ');
14 read(car);
15 //Damos la condición de error.
16 error:= (ord(car)<48) or (ord(car)>57);
17
18 If not error then
19 entero:= ord(car)-ord('0');
20
21 //Leemos hasta el fin de línea o hasta que haya error.
22 while (not eoln) and (not error) do
23 begin
24 //Leemos el siguiente caracter.
25 read(car);
26
27 //Verificamos que el caracter sea uno numérico.
28 error:= (ord(car)<48) or (ord(car)>57);
29
30 If not error then
31 entero:= (entero*10)+(ord(car)-ord('0'));
32 end;
33
34 If not error then
35 writeln('El entero ingresado es: ',entero)
36 else
37 writeln('No ha introducido un entero.');
38
39 END.

Como primer ejercicio tienen que entender este programa y modificarlo para que funcione con REPEAT.

No continúen hasta no ser capaces de resolver todos estos ejercicios.

Ejercicio2: Determinen cuáles de los siguientes segmentos de código producen la misma salida al ser
ejecutados:

a) b)
i := 1; i := 1;
j := 2; j := 1;
REPEAT WHILE (i <= 3) AND (j <= 2) DO
Write(i, j); BEGIN
i := i + 1; Write(i, j + 1);
j := j + 1 i := i + 1;
UNTIL j <= 3; j := j + 1
END;
c)
i := 1;
REPEAT
Write(i, i + 1);
i := i + 1
UNTIL i <= 3;

Vladimir Rodríguez 51
Programación estructurada en Pascal

Ejercicio3: Se desea tener un programa que calcule el saldo de una cuenta. Supongan que los datos son leídos
de la entrada estándar y que constan de renglones, cada uno de los cuales contiene una letra en la primera columna,
seguida de un valor real. El último renglón contiene únicamente la letra X en la columna uno. El primer renglón
contiene la letra A y el saldo anterior de una cuenta de cheques. Los demás renglones contienen la letra D y el importe
de un depósito o la letra R y el importe de un retiro. Escriban un programa que determine el saldo exacto de la cuenta
después de procesar las transacciones. Asumimos que las entradas serán siempre correctas.

Ejemplo:

A 1200.35
D 64.12
R 390.00
R 289.67
D 13.02
R 51.07
X
El saldo final es 546.75

Ejercicio4: Dado un fragmento de texto que debe ser leído de la entrada estándar, todo en una línea, y
terminado por el caracter $ (centinela), determine y exhiba las consonantes y vocales que aparecen duplicadas en
forma contigua. Por ejemplo, el texto “llama al chico que lee$” tiene una consonante doble (ll) y una vocal doble
(ee) y se debería desplegar: “ll ee”. (Todas las letras son minúsculas).

Ejercicio5: Realicen las funciones de una calculadora simple. Los datos de entrada serán una secuencia de
dígitos enteros y los operadores +, *, / y -, seguida de un signo =. Hagan caso omiso de los espacios en blanco. Los
operadores se aplican en el orden en que aparecen en los datos de entrada, o sea que olvídense de la precedencia, y
producen resultados enteros, o sea, si bien se ingresa el operador de la división con el símbolo /, el comportamiento es
el de DIV. Asuman que se ingresa al menos un número.

Ej. de entrada: 4 + 3 / 2 * 8 - 4 =
Ej. de salida: 20

Como ven, las operaciones se fueron realizando en el orden en el que aparecían. Primero 4+3 que es 7, eso lo
dividimos entre 2 (div) que es 3, luego eso multiplicado por 8 que es 24 y luego eso menos 4 que es 20. Una
verdadera calculadora habría hecho primero 3/2 que con DIV da 1 y luego eso por 8 que da 8. Luego haría 4+8-4 que
es igual a 8. Sin embargo programar eso requiere más herramientas de las que poseen.

Ejercicio6 (DIFICIL): Escriban un programa que lea una letra y una oración de la entrada estándar (de una
sola línea), y calcule la cantidad de palabras que terminan con la letra y están en la oración dada. Asuman que la
oración siempre terminará con un punto y que las palabras estarán separadas solo por espacios (no habrá comas ni
nada de eso). Asuman también que la oración tendrá siempre al menos una palabra.
Ejemplos:

Letra: s
Oración: La cualidad indispensable para un buen cocinero es la puntualidad
pero es también la de los invitados.
Salida: 4

Letra: r
Oración: Un discípulo de quien jamás se pide nada que no pueda hacer nunca
hace todo lo que puede.
Salida: 1

Letra: o
Oración: No basta tener buen ingenio lo principal es aplicarlo bien.
Salida: 4
Modifiquen su programa para que cuente las palabras que comienzan con la letra dada.

Este es un ejercicio que los hará pensar y luchar mucho. Intenten hacerlo bajo todos los medios posibles.
Todo lo dado hasta ahora basta y sobra para lograrlo. Si no logran realizarlo escríbanme y les enviaré la solución
para que la estudien, pero no tiene caso leer un programa ya hecho si ustedes no intentan realizarlo primero
poniendo todas las ganas de aprender. Este manual es para ustedes y requiere todo su esfuerzo. No hay otro medio
para aprender a programar. Mi esfuerzo fue escribir esto, ustedes practiquen y practiquen, pregúntenme sus dudas a
mi cacilla de correo y sigan intentando. Los frutos se verán al final.

Vladimir Rodríguez 52
Programación estructurada en Pascal

TERCERA PARTE

Introducción a los TIPOS definidos por el programador.


Subrangos y Arreglos.

Vladimir Rodríguez 53
Programación estructurada en Pascal

Introducción:
Hasta ahora hemos visto, en las dos primeras partes de este tutorial, como crear programas que puedan hacer
una cosa u otra en función de ciertas condiciones, como hacer que repitan ciertas instrucciones también de forma
condicional, entre otras cosas. En todos esos programas hemos usado variables de distintos tipos, sin embargo
siempre han sido tipos primitivos de Pascal, o sea, los que ya están predefinidos por el lenguaje. Sin embargo es
posible que el programador defina tipos propios que pueden contener datos más complejos que simples números,
caracteres o cadenas.
Obviamente esto no es totalmente libre, pero dada nuestra imaginación podemos crear tipos realmente muy
complejos y que contengan mucha información. Los posibles tipos que podemos crear son: Subrangos, Arreglos,
Conjuntos, Enumerados, Registros y Punteros. Todos excepto Punteros, al igual que los tipos primitivos,
corresponden a tipos estáticos de Pascal. Esto significa que declarada una variable de uno de esos tipos se asignará de
la memoria el espacio suficiente para albergar el mayor valor posible. Este espacio de memoria estará en uso aunque
nosotros no usemos la variable declarada o aunque usemos solo una parte de la información que esta pueda guardar.
Este espacio de memoria se asigna ya al compilar el programa, o sea, en tiempo de compilación.
Los Punteros corresponden a un tipo dinámico. Esto es que, podremos pedir memoria cuando la necesitemos y
devolverla cuando ya no la usemos. Este será el último tema de este tutorial.

Vladimir Rodríguez 54
Programación estructurada en Pascal

Subrangos:

Muy bien, el primer tipo a definir por el usuario que veremos es llamado subrango. Este es el tipo más
sencillo de declarar. Consiste en crear un Subrango de uno de los tipos primitivos ordinales de Pascal. Por ejemplo,
un Subrango de los enteros podrían ser los naturales, ya que estos son lo enteros de 0 en adelante (algunos los toman
de 1 en adelante).
Los tipos se declaran antes que las variables pero luego que las constantes. Para ello debemos indicar
mediante la palabra reservada TYPE que haremos declaración de tipos. Veamos un ejemplo de declaración de
subrangos:

Type
Naturales= 0..MAXINT;
Mes= 1..12;
Decimal= 0..9;
Minusculas= 'a'..'z';
Digito= '0'..'9';
Mayusculas= 'A'..'Z';

Aquí hemos declarado seis tipos Subrango diferentes. Como ven la sintaxis es bien sencilla, luego de la
palabra TYPE se declaran todos los tipos necesarios de esta manera:
Identificador= valor_inicial..valor_final;

En este ejemplo tenemos el tipo Naturales que contendrá valores enteros entre 0 y MAXINT. Luego está
el tipo Mes que contendrá enteros entre 1 y 12. Decimal contendrá valores entre 0 y 9. Luego vemos tres subrangos
de caracteres: Minusculas que contendrá los caracteres de la a a la z. Esto es porque las letras están ordenadas
según la Tabla Ascii ascendentemente y sabemos que ‘a’<’z’. Digito contendrá los caracteres del 0 al 9. Notar que
Digito contiene los caracteres numéricos y Decimal los valores enteros de esos números. No es lo mismo.
Finalmente tenemos el tipo Mayusculas que contiene caracteres de la A a la Z.

Dado que estos son tipos, para usarlos debemos declarar variables de ese tipo:

Type
Naturales= 0..MAXINT;
Mes= 1..12;
Decimal= 0..9;
Minusculas= 'a'..'z';
Digito= '0'..'9';
Mayusculas= 'A'..'Z';

Var
letraMin: Minusculas;
numero: Naturales;
letraMay: Mayusculas;

Como ven es la misma sintaxis que para declarar una variable de tipo primitivo, le damos un identificador,
dos puntos y luego el nombre del tipo. Esto significa que la variable letraMin podrá tomar valores de la a a la z ya
que es del tipo Minusculas. Como el Subrango Minusculas corresponde a un Subrango de caracteres,
letraMin se comportará como una variable char con la diferencia de que no podrá tomar valores fuera del rango
indicado por Minusculas. Si esto sucede tendremos un error en tiempo de ejecución. Si han activado Range
checking como se los indiqué al inicio del tutorial, el compilador verificará esto en la medida de lo posible y se los
advertirá, pero no siempre podrá hacerlo. Por ejemplo, si ustedes hacen read(letraMin) el compilador no podrá
asumir que el usuario puede ingresar un carácter fuera del rango y por tanto compilará correctamente, pero si hacen,
letraMin:= ‘A’ les advertirá su error. Hay más casos de esto, pero lo verán en la práctica.
Lo mismo sucede para los subrangos Decimal, Mes y Naturales, que son subrangos de integer. Las
variables de estos tipos funcionarán como variables enteras con la diferencia de que deben tomar valores dentro de
los rangos establecidos.

Visto de esta manera no puedo incentivarlos mucho con el uso de los subrangos, sin embargo veremos que
resultan útiles para definir Arreglos, que es el tema que viene a continuación.
Lo que puedo decirles es que al definir un Subrango ustedes podrán estar seguros de que sus variables tendrán
los valores adecuados. Claro que ustedes deben encargarse de que estas no tomen valor por fuera del rango
establecido.

Vladimir Rodríguez 55
Programación estructurada en Pascal

Arreglos:

Declaración:

Muy bien, ahora sí se viene algo más interesante. Este es un tema muy importante y deberán aprenderlo bien,
ya que los arreglos son estructuras usadas para casi todo y en la mayoría de los lenguajes.
Un arreglo es una tabla compuesta por celdas que contienen, todas, valores de un mismo tipo.

*------*------*------*------*-----*-----*-----*-----*-----*
| 23 | 34 | 0 | -12 | 6 | 9 | 11 | -2 | 34 |
*------*------*------*------*-----*-----*-----*-----*-----*
1 2 3 4 5 6 7 8 9

Ese es un ejemplo de un arreglo de enteros compuesto por nueve celdas. Cada celda tiene un valor distinto
(pueden no serlo) y cada una es independiente de las demás. Como pueden ver todas las celdas están numeradas del 1
al 9. Estos valores son llamados subíndices y son los que usaremos para dirigirnos a una celda específica.

Veamos primero cómo se declara un arreglo:

Identificador= array[Subrango] of tipo;

Debemos indicar un nombre para el arreglo (identificador), luego, al igual que para los subrangos va un signo
de igual (=) y la palabra reservada array (arreglo en inglés) seguida inmediatamente por un Subrango entre
paréntesis rectos [] el cual indicará la dimensión del arreglo (cuantas celdas tendrá). Luego colocamos la palabra
reservada of y un tipo para los valores que habrá en cada celda. Este tipo puede ser uno primitivo de Pascal o uno
definido anteriormente por el programador. Veamos algunos ejemplos:
El arreglo dibujado arriba podría estar declarado como:
Arreglo1= array[1..9] of integer;

En este ejemplo le dimos el nombre Arreglo1. Declaramos directamente entre los paréntesis rectos un
Subrango. La dimensión de ese Subrango indica la dimensión del arreglo, en este caso el Subrango va de 1 a 9 por
tanto contiene 9 elementos. Esto indica que el arreglo tendrá nueve celdas. Es importante notar que esto indica solo la
dimensión del arreglo y no el tipo de datos que contendrá, o sea, Arreglo1 no contendrá datos que solo vayan de 1
a 9 sino que contendrá cualquier entero en sus celdas ya que su tipo está declarado luego de of y es integer.
El Subrango entre paréntesis rectos, además de indicar cuántas celdas tendrá el arreglo, también indica cómo
se numeran. Arreglo1 tendrá 9 celdas numeradas del uno al nueve, sin embargo, de haberse declarado
Arreglo1= array[10..19] of integer;
sería igual al anterior solo que sus nueve celdas estarían numeradas del 10 al 19, o de haberse declarado de esta
manera
Arreglo1= array[‘a’..’i’] of integer;
seguiría teniendo nueve celdas solo que estarían identificadas de la a a la i.
Lo más común es numerarlas del 1 en adelante y es lo recomendable.
Tal como hemos declarado esos arreglos estamos declarando sus dimensiones con subrangos anónimos, o sea,
declarando estos justo dentro de los paréntesis rectos sin darles nombre alguno. Sin embargo también es posible
colocar dentro de estos paréntesis un Subrango ya declarado anteriormente. Por ejemplo:

Type
Decimal= 0..9;
Arreglo1= array[Decimal] of integer;

Como ven, teniendo declarado antes el Subrango Decimal es posible utilizarlo para definir la dimensión y
numeración del arreglo. En este caso Arreglo1 tendrá 10 celdas numeradas del 0 al 9.
También es posible hacer esto

Type
Decimal= 0..9;
Arreglo1= array[Decimal] of Decimal;

En este caso, el arreglo tiene 10 celdas numeradas del 0 al 9 y cada una puede contener datos del tipo
Decimal. Siempre es posible utilizar algún tipo para definir otro sí y solo sí el tipo a utilizar está definido
anteriormente. No sería posible utilizar Decimal para definir el arreglo si declaráramos este Subrango más abajo
que el arreglo.

Un arreglo es un tipo y por tanto va declarado luego de la palabra Type tal como se ve en estos dos ejemplos.
Veamos ahora más ejemplos de arreglos:

Vladimir Rodríguez 56
Programación estructurada en Pascal

Type
rango = 33..90;
arr1 = array [char] of integer; (* 256 celdas *)
arr2 = array [33..90] of real;
arr3 = array [integer] of char; (* demasiado grande! *)
arr4 = array [rango] of boolean;

Tenemos un Subrango de enteros que va de 33 a 90. Luego tenemos arr1 que es un arreglo de 256 celdas
(cantidad total de caracteres) cuyos índices van de ‘a’ a ‘z’ donde cada una puede contener cualquier entero. arr2
que es un arreglo de 57 celdas numeradas del 33 al 90 donde cada una puede contener cualquier real. Luego tenemos
arr3 que pretendería ser un arreglo de unas 65536 celdas numeradas desde el -32768 hasta el 32767 donde cada una
podría contener caracteres. Este es un arreglo exorbitantemente grande, ocuparía muchísima memoria y provocaría
muchos problemas. Además no es para nada ético crear este tipo de estructuras.
Finalmente tenemos arr4 que tiene 57 celdas numeradas del 33 al 90 donde cada una es un booleano que
puede valer TRUE o FALSE.

Utilización de arreglos:

Hasta ahora solo hemos visto como se declara un arreglo y lo que significan las distintas declaraciones, sin
embargo hace falta trabajar con ellos, y su sintaxis es un poco distinta a lo que venimos acostumbrados. Veremos un
primer ejemplo donde crearemos un arreglo de enteros de 10 celdas cuyos índices van del 1 al 10 y a cada celda le
daremos el mismo valor que su índice, o sea, a la primera le daremos el valor 1, a la segunda el 2 y así sucesivamente:

1 PROGRAM arreglos;
2
3 Type
4 arr= array[1..10] of integer;
5
6 Var
7 arreglo1: arr;
8
9 BEGIN
10 arreglo1[1]:= 1;
11 arreglo1[2]:= 2;
12 arreglo1[3]:= 3;
13 arreglo1[4]:= 4;
14 arreglo1[5]:= 5;
15 arreglo1[6]:= 6;
16 arreglo1[7]:= 7;
17 arreglo1[8]:= 8;
18 arreglo1[9]:= 9;
19 arreglo1[10]:= 10;
20 END.

En la línea 3 indicamos que comenzará la declaración de tipos para, luego, en la línea 4 declarar el tipo arr
que será un arreglo de 10 celdas numeradas del 1 al 10 del tipo entero. Como arr es un tipo su propósito es declarar
variables de ese tipo, por lo tanto en la línea 7 declaramos la variable arreglo1 del tipo arr. Podemos, al igual
que con los tipos primitivos, declarar tantas variables como queramos de cualquier tipo que nosotros mismos
definamos; en este caso tenemos solo una, pero podría tener más y cada una sería independiente.
Bien, en la línea 9 comienza nuestro programa, el cual consta de diez líneas de asignación donde en cada una
le damos un valor a cada celda del arreglo. Veamos esto con detalle, es bien sencillo. Cada celda del arreglo funciona
como una variable independiente, por tanto, en ese arreglo tenemos diez variables del tipo integer. Para referirnos a
una celda debemos dar el nombre de nuestro arreglo (no el nombre del tipo sino el de la variable de ese tipo) seguido
por el índice entre paréntesis rectos de la celda a la que queremos ir. De este modo, en la línea 10, al escribir
arreglo1[1]:= 1 estamos diciendo que vaya a la primera celda de arreglo1 y le asigne el valor 1. La sintaxis
genérica sería
Variable_del_tipo_arreglo[indice_de_la_celda]
con lo cual nos referiríamos a cualquier celda. Recordar que cada una es una variable independiente, por tanto esa
declaración es como si fuera el nombre de la variable y funciona igual que cualquier otra variable, valga la
redundancia.
Todo lo que sigue del programa es asignar a las celdas restantes el valor que queremos y termina nuestro
programa. Sin embargo no es muy útil tener que escribir instrucción por instrucción para dar un valor a cada celda.
Imaginen un arreglo de 1000 celdas, tendríamos un programa de 1000 líneas solo en asignaciones. Veamos el mismo
programa pero asignando los mismos valores a cada celda de una forma más inteligente:

Vladimir Rodríguez 57
Programación estructurada en Pascal

1 PROGRAM arreglos;
2
3 Type
4 arr= array[1..10] of integer;
5
6 Var
7 arreglo1: arr;
8 i: integer;
9
10 BEGIN
11 For i:=1 to 10 do
12 arreglo1[i]:= i;
13 END.

Hemos sustituido las 10 líneas de asignación por una instrucción FOR que va de 1 a 10. Fíjense que hemos
colocado la variable de control i dentro los paréntesis rectos que indican el índice de nuestras celdas. De este modo,
cuando i valga 1 estaremos hablando de la primera celda, cuando i pase a valer 2 estaremos hablando de la segunda
celda, y así sucesivamente hasta 10. En este caso hemos asignado a cada celda el mismo valor de su índice, pero esto
podría no ser así. Este arreglo dibujado sería:

*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
1 2 3 4 5 6 7 8 9 10

Ahora veamos lo mismo pero asignando el doble del índice a cada celda:

1 PROGRAM arreglos;
2
3 Type
4 arr= array[1..10] of integer;
5
6 Var
7 arreglo1: arr;
8 i: integer;
9
10 BEGIN
11 For i:=1 to 10 do
12 arreglo1[i]:= i*2;
13 END.

De este modo arreglo1 ahora quedaría así:

*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 |
*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
1 2 3 4 5 6 7 8 9 10

Dentro de los paréntesis rectos que indican el índice del arreglo es posible, como hemos visto ya, colocar el
valor del índice, una variable del tipo correcto o, como no hemos visto aún, una expresión que dé cómo resultado un
valor del tipo correcto y que esté dentro del rango de índices posibles.

Es muy importante que lo que esté dentro de los paréntesis rectos nunca exceda el rango en que está numerado
el arreglo. Si en este ejemplo nosotros escribiéramos
For i:=1 to 11 do
arreglo1[i]:= i*2;

se produciría un error cuando i alcanzara el valor 11 y el programa se cerraría abruptamente ya que la celda 11 no
existe. Si han activado Range checking lo más probable es que el compilador les avise antes, pero esto no es
siempre seguro ya que no siempre es detectable que podemos estar saliéndonos del arreglo.

Veamos un nuevo ejemplo del mismo programa, solo que ahora los índices serán caracteres y a cada celda le
asignamos el valor del ordinal de su índice. No olviden que el arreglo es de enteros.

Vladimir Rodríguez 58
Programación estructurada en Pascal

1 PROGRAM arreglos;
2
3 Type
4 arr= array[‘a’..’j’] of integer;
5
6 Var
7 arreglo1: arr;
8 i: char;
9
10 BEGIN
11 For i:=’a’ to ‘j’ do
12 arreglo1[i]:= ord(i);
13 END.

De este modo nuestro arreglo quedaría así:

*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
| 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
‘a’ ‘b’ ‘c’ ‘d’ ‘e’ ‘f’ ‘g’ ‘h’ ‘i’ ‘j’

Hacer WRITE y READ de un arreglo:

Si quisiéramos desplegar un arreglo en la pantalla, o sea, que se nos muestren todos sus valores, debemos
escribir celda por celda. En el ejemplo anterior teníamos el arreglo llamado arreglo1. Como primer impulso para
escribirlo en pantalla uno tendería a hacer algo como esto

write(arreglo1);

y sin embargo eso no es posible. De hacerlo tendrán un error en tiempo de compilación en el cual el
compilador se les quejará por no poder escribir o leer variables de ese tipo. Esto es porque un arreglo es una
estructura de datos y no un valor específico. Por este motivo es que debemos escribir celda por celda. No se olviden
que cada celda es como una variable más y funciona del mismo modo.
Veamos entonces como mostrar el arreglo de nuestro ejemplo anterior:

1 PROGRAM arreglos;
2
3 Type
4 arr= array[‘a’..’j’] of integer;
5
6 Var
7 arreglo1: arr;
8 i: char;
9
10 BEGIN
11 For i:=’a’ to ‘j’ do
12 arreglo1[i]:= ord(i);
13
14 For i:=’a’ to ‘j’ do
15 Write(arreglo1[i],’ ‘);
16 END.

La única diferencia entre este programa y el anterior es que agregamos un FOR que en cada iteración escribe
el valor de una de las celdas. Verifiquen esto ustedes mismos.

Ejercicio: Modifiquen este programa para que con un solo FOR asigne los valores al arreglo y lo muestre en
pantalla.

Del mismo modo, si quisiéramos hacer read de un arreglo para que el usuario ingrese los valores de cada
celda debemos hacerlo para cada una por separado.

Ejercicio: Realicen un programa en el cual exista un arreglo de enteros de cinco celdas de modo que el
usuario sea quién ingrese los valores para cada celda. Deben utilizar una instrucción FOR para ello.

Vladimir Rodríguez 59
Programación estructurada en Pascal

Declaración anónima de un arreglo:

Los arreglos que hemos visto hasta ahora los hemos declarado como tipos de modo que podemos declarar
muchas variables de ese tipo. Sin embargo, a veces sucede que sabemos que vamos a usar un único arreglo en nuestro
programa y nada más. Dado este caso no es necesario declarar el arreglo como tipo sino hacerlo de forma anónima,
directamente como variable. Veamos esto con el programa anterior ya que tenemos un único arreglo en este:

1 PROGRAM arreglos;
2
3 Var
4 arreglo1: array[‘a’..’j’] of integer;
5 i: char;
6
7 BEGIN
8 For i:=’a’ to ‘j’ do
9 arreglo1[i]:= ord(i);
10
11 For i:=’a’ to ‘j’ do
12 Write(arreglo1[i],’ ‘);
13 END.

Como ven este programa es igual al anterior solo que hemos quitado la declaración de tipos y hemos
declarado el arreglo directamente como variable. De este modo solo se usará como arreglo1 y no como tipo por lo
cual no podremos declarar variables como en los casos anteriores.

No está mal si declaran siempre los arreglos como tipo, pero tengan en cuenta que un arreglo como tipo ocupa
más memoria que uno declarado anónimamente. Si han declarado un tipo array el programa guardará memoria para
“recordar” que existe un tipo definido por ustedes y qué datos puede contener y luego también guardará memoria
para cada variable que declaren de ese tipo. Si lo hacen de forma anónima solo guardará memoria para esa variable y
punto.
Un programa que utiliza mucha memoria es menos eficiente, al igual que uno que realiza demasiadas
instrucciones o tareas. Siempre es importante tratar de que el programa haga el menor trabajo posible para que sea
más veloz y requiera menos recursos de la computadora para trabajar.

Yo no voy a dedicarme a enseñarles acerca de la eficiencia de los programas ya que este tutorial está dirigido
a personas que recién están aprendiendo, pero ya vayan teniendo en cuenta que este es un aspecto muy importante.
De todos modos en algunos casos nombraré la mejor forma de realizar una tarea eficientemente.

Recorriendo arreglos:

Ya sabemos como crear un arreglo y acceder a sus celdas para asignarles valores, leer estos desde la entrada o
escribirlos. También sabemos que cada celda funciona como una variable independiente y que siempre podemos
acceder a la que queramos si sabemos como están numeradas las celdas. Es por esto que conviene siempre que los
índices vayan de 1..N, o sea de 1 hasta N donde N es la cantidad de celdas del arreglo. Esto ayuda a evitar
confusiones y hace todo mucho más entendible. Sin embargo pueden hacerlo de otra manera si consideran que para
cierto caso les resulta más útil.

Muchas veces resulta necesario recorrer un arreglo, ya sea para buscar un elemento de él o para trabajar de
algún modo con los valores de sus celdas, de otro modo ¿para qué lo habríamos creado entonces?

Aunque les dejé esto como ejercicio, espero lo hayan hecho antes de llegar aquí, veremos un ejemplo donde el
usuario ingresa 10 valores enteros para un arreglo y luego nosotros desplegaremos éstos para mostrárselos y además
le mostraremos la suma de ellos:

1 PROGRAM sumaCeldas;
2
3 Const
4 N= 10; //Dimensión del arreglo.
5
6 Type
7 arr= array[1..N] of integer;
8
9 Var
10 arreglo1: arr;
11 i, suma: integer;

Vladimir Rodríguez 60
Programación estructurada en Pascal

12
13 BEGIN
14 //Mensaje para el usuario.
15 write('Ingresa ‘,N,’ enteros: ');
16
17 //Leemos un valor para cada celda.
18 For i:=1 to N do
19 read(arreglo1[i]);
20
21 //Dejamos una línea en blanco.
22 writeln;
23
24 //Mensaje al usuario. Mostramos su ingreso.
25 write('Usted ha ingresado: ');
26 For i:=1 to N do
27 write(arreglo1[i],' ');
28
29 //Iniciamos suma en 0 ya que aún no hemos sumado nada.
30 suma:=0;
31
32 //Recorremos todo el arreglo sumando los valores a suma.
33 For i:=1 to N do
34 suma:= suma + arreglo1[i];
35
36 //Mostramos el resultado al usuario.
37 writeln;
38 writeln('La suma de los valores es: ',suma);
39 END.

Lo primero a destacar en este ejemplo es el uso de la constante N cuyo valor es 10. Es simplemente la que
usaremos para declarar el Subrango que declara la dimensión del arreglo. De este modo la usaremos también en las
iteraciones FOR para recorrer todas las celdas del arreglo. Si luego debiéramos modificar nuestro programa para que
el usuario ingrese 20 valores en vez de 10 solo debemos cambiar el valor de N en su declaración de la línea 4 y todo
funcionará perfectamente. De lo contrario deberíamos cambiar un 10 por un 20 cada vez que iteráramos con el
arreglo, al declararlo, etc.
En la línea 17 tenemos un FOR que leerá un valor para cada celda del arreglo. Dada esa declaración el usuario
puede ingresar de a un valor he ir presionando enter, o ingresar los 10 valores separados por espacio en una misma
línea y presionar enter al final. Esto es posible ya que hemos usado el procedimiento read que lee un valor y deja el
cursor en la misma línea. De haber usado readln deberíamos ingresar un valor por línea.
Les dejo el resto del programa a ustedes.

Ahora veremos un ejemplo donde el usuario ingresa 10 valores enteros en un arreglo y el programa le
mostrará el mayor de todos ellos:

1 PROGRAM arreglos;
2
3 Const
4 N= 10; //Dimensión del arreglo.
5
6 Type
7 arr= array[1..N] of integer;
8
9 Var
10 arreglo1: arr;
11 i, mayor: integer;
12
13 BEGIN
14 //Mensaje para el usuario.
15 write('Ingresa ',N,' enteros: ');
16
17 //Leemos un valor para cada celda.
18 For i:=1 to N do
19 read(arreglo1[i]);
20
21 //Dejamos una línea en blanco.
22 writeln;
23

Vladimir Rodríguez 61
Programación estructurada en Pascal

24 //En un principio el mayor valor es el primero.


25 mayor:= arreglo1[1];
26
27//Ahora recorremos el resto del arreglo buscando el mayor valor.
28 For i:=2 to N do
29 begin
30 If arreglo1[i]>mayor then
31 mayor:= arreglo1[i];
32 end;
33
34 //Informamos al usuario.
35 writeln('El mayor valor ingresado es: ',mayor);
36 END.

Lean ustedes mismos este código. Seguro son capaces de entenderlo muy bien.

Búsqueda de un elemento:

En este ejemplo veremos un programa en el que el usuario ingresa 10 enteros que guardaremos en el arreglo y
luego le pediremos que ingrese otro valor. Buscaremos ese valor en el arreglo. Si está ahí lo notificaremos y en caso
contrario notificaremos que no está:

1 PROGRAM BusquedaArreglo;
2
3 Const
4 N= 10; //Dimensión del arreglo.
5
6 Type
7 arr= array[1..N] of integer;
8
9 Var
10 arreglo1: arr;
11 i, valorBuscado: integer;
12 exito: boolean;
13
14 BEGIN
15 //Mensaje para el usuario.
16 write('Ingresa ',N,' enteros: ');
17
18 //Leemos un valor para cada celda.
19 For i:=1 to N do
20 read(arreglo1[i]);
21
22 //Dejamos una línea en blanco.
23 writeln;
24
25 //Leemos el valor a buscar en el arreglo.
26 write('Ingresa el valor que deseas buscar: ');
27 readln(valorBuscado);
28
29 //Inicializamos nuestra variable índice en 0.
30 i:=0;
31 {Iteramos hasta encontrar el valor o hasta
32 recorrer todo el arreglo sin hallarlo}
33 Repeat
34 //Aumentamos un índice.
35 i:= i + 1;
36 exito:= arreglo1[i]=valorBuscado;
37 Until (exito) or (i=N);
38
39 writeln;
40
41 //Mostramos el mensaje correspondiente.
42 If exito then
43 writeln('El valor está en el arreglo.')
44 else
45 writeln('El valor no está en el arreglo.');
46 END.

Vladimir Rodríguez 62
Programación estructurada en Pascal

Lo importante aquí está a partir de la línea 30 donde inicializamos i en 0 ya que será nuestro índice. En la
línea 33 declaramos un REPEAT que aumentará nuestro índice de búsqueda y luego se fijará si el valor buscado es
igual al de la celda en que estamos posicionados. En ese caso exito será true y la iteración terminará. En caso
contrario volveremos a aumentar i en 1 y nos fijaremos en la nueva posición. La iteración terminará al encontrar el
elemento o al recorrer todo el arreglo sin hallarlo.
Es importante destacar el uso del REPEAT aquí. Como primera cosa sabemos que iteraremos al menos una
vez ya que debemos fijarnos al menos en la primera celda del arreglo, por eso usé un REPEAT y no un WHILE.
Alguno podrá preguntarse ¿y por qué no usaste un FOR para recorrer el arreglo como has hecho hasta ahora? Bien,
veamos como quedaría ese REPEAT si fuera un FOR

For i:=1 to N do
exito:= arreglo1[i]=valorBuscado;

If exito then
Writeln(‘El valor está en el arreglo.’)
Else
Writeln(‘El valor no está en el arreglo.’);

Con el FOR recorreremos todo el arreglo siempre. La bandera booleana se volverá true al hallar el elemento.
Funciona perfectamente al igual que el REPEAT, ahora piensen esto. Imaginen que tenemos un arreglo de 500 celdas
y el valor que buscábamos estaba en la celda número 86. Con el REPEAT recorreremos solo las primeras 86 celdas
del arreglo y nos detendremos ya que al encontrar el elemento no nos interesa lo demás. Con el FOR, al encontrar el
elemento la bandera será true pero aún así recorreremos las 500 celdas. Esto hace al programa trabajar de más cuando
no es necesario y le resta mucha eficiencia. Piensen en un arreglo de miles y miles de celdas, por ejemplo, una base
de datos de Google en donde están guardadas casi todas las webs del mundo. ¿Sería mejor recorrer la base de datos
hasta encontrar el elemento o recorrerla toda siempre?

Es importante que comprendan esto. Aunque ya les dije que no les haría mucho hincapié con esto de la
eficiencia, también dije que en ciertas ocasiones mencionaría el modo de realizar ciertas tareas de la forma más
eficiente posible.

Deben notar que el programa anterior se detendrá ante el primer encuentro con el valor buscado sin interesarse
en si se repite luego.

En la página siguiente hay una serie de ejercicios. No continúen con el tutorial hasta que sean capaces de
realizarlos todos, o de lo contrario, no podrán con los temas que vendrán a continuación

Ejercicio1: Escriban un programa que lea diez enteros de la entrada estándar y guarde estos en un arreglo. El
programa debe indicar el mayor de ellos y el índice de la posición en la que aparece, así como también, el menor de
ellos y su posición. En caso de que se repita un valor mostrarán el índice de la primera celda en que aparezca.
Asumimos que siempre se ingresan enteros en la entrada estándar.

Ejemplo1:

Ingrese 10 enteros: 2 3 1 4 5 10 6 7 8 9
El mayor entero es 10 en la posición 6
El menor entero es 1 en la posición 3

Ejemplo2:

Ingrese 10 enteros: 2 3 2 20 5 20 6 7 8 9
El mayor entero es 20 en la posición 4
El menor entero es 2 en la posición 1

Ejercicio2: Este ejercicio conlleva cierta dificultad, sobretodo porque trabajarán con dos arreglos a la vez,
cada uno de distinto tamaño. Dada la definición de tipo para representar cadenas de caracteres de largo M y N :

CONST N = . . .;
CONST M = . . .; { M < N }
. . .
TYPE
CadenaM = ARRAY[1..M] Of Char;

Vladimir Rodríguez 63
Programación estructurada en Pascal

CadenaN = ARRAY[1..N] Of Char;

Implementen un programa que lea dos cadenas de la entrada estándar de largo M y N respectivamente, y
determine si la primer cadena ocurre como parte de la segunda cadena. El programa debe funcionar para cualquier
valor positivo que puedan tomar M y N, considerando la restricción M < N. Ustedes mismos definan los valores de las
constantes.Ejemplo de entrada para N=6, M=3:

tor
totora

Ejemplo de salida:

El texto 'tor' se encuentra dentro del texto 'totora'.

Ejemplo de entrada:

tos
totora

Ejemplo de salida:

El texto 'tos' no se encuentra dentro del texto 'totora'.

Ejercicio3: Tiene una dificultad similar al anterior. Dada la definición de tipo para representar cadenas de caracteres
de largo N y M:

CONST N = . . .;
CONST M = . . .;
. . .
TYPE
CadenaM = ARRAY[1..M] Of Char;
CadenaN = ARRAY[1..N] Of Char;

1. Escriban un programa que lea dos cadenas de largo M y N respectivamente, e imprima un mensaje en la
salida estándar indicando si alguna letra en la primera palabra ocurre en la segunda.
2. Escriban un programa que lea dos cadenas de largo M y N respectivamente, y determine si todas las letras
en la primera palabra ocurren en la segunda.

Como ya saben, siempre que un ejercicio les de verdaderos problemas no tienen más que escribirme a
mstrvladi@hotmail.com explicándome sus dudas y les responderé en breve. Siempre intenten todo lo que puedan y
prueben bien sus programas antes de dar por terminado un ejercicio.

Vladimir Rodríguez 64
Programación estructurada en Pascal

Arreglos multidimensionales:

Los arreglos que hemos visto hasta ahora son unidimensionales, o sea, de una única dimensión. Se dice esto
ya que se requiere una única coordenada (índice) para ubicar una celda del mismo. Sin embargo es posible definir
arreglos de dos dimensiones (2D), de tres dimensiones (3D) y más. Aquí veremos ejemplos de arreglos de hasta tres
dimensiones ya que no requerimos de más para trabajar, sin embargo muchos programas usan arreglos de N
dimensiones. Como nosotros existimos en un espacio de dimensión 3 solo podemos ver gráficamente arreglos de
dimensión 3 o menor.

Arreglos bidimensionales:

Comencemos por ver arreglos de dos dimensiones. Estos dibujarán una tabla de datos del mismo tipo, también
conocidas matemáticamente como matrices.

Hay dos maneras de declarar arreglos bidimensionales, una es declarar un arreglo de un arreglo, o sea, un
arreglo cuyas celdas sean también arreglos. Esta es la forma más fea de hacerlo y la que menos recomiendo, pero
existe. Veámoslo en un ejemplo:

1 PROGRAM BIDIMENSIONAL;
2
3 Type
4 arr1= array[1..5] of integer;
5 Tabla= array[1..7] of arr1;
6
7 Var
8 i, j: integer;
9 matriz: Tabla;
10
11 BEGIN
12 For i:= 1 to 7 do
13 For j:= 1 to 5 do
14 matriz[i,j]:= 0;
15 END.

En la línea 4 vemos un tipo arr1 que es un arreglo de 5 celdas de enteros. En la línea 5 vemos el tipo Tabla
que es un arreglo de 7 celdas del tipo arr1, o sea que cada celda es un arreglo de 5 celdas. Si dibujáramos Tabla al
igual que lo hicimos antes tendríamos 7 celdas del 1 al 7, esto estaría indicando cuantas filas tiene nuestra tabla. Si
desplegamos cada celda de Tabla estaríamos abriendo hacia la derecha 5 lugares con lo cual estaríamos indicando
las columnas de nuestra tabla.

Columnas(j)
0 0 0 0 0 Filas(i)
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
Ese sería el dibujo de Tabla, como ven hay 7 filas (líneas horizontales) y 5 columnas (líneas verticales). El 0
corresponde a la posición (3,3) de la tabla ya que está en la fila 3 y en la columna 3. Siempre para una tabla se
especificarán las coordenadas de una celda nombrando primero el número de filas y luego el de columnas.

En la línea 9 del ejemplo declaramos la variable matriz del tipo Tabla. El trabajo con los arreglos
bidimensionales es idéntico al de los arreglos unidimensionales. Cada celda es una variable independiente y para
acceder a cada una simplemente nombramos nuestro arreglo y damos entre paréntesis rectos las coordenadas de
nuestra celda separadas por comas. La forma genérica sería así:
Variable_arreglo[fila,columna]

de este modo, si quisiéramos ir a la celda que está en la fila 7 y en la columna 4 deberíamos escribir matriz[7,4].

Vladimir Rodríguez 65
Programación estructurada en Pascal

En nuestro programa de ejemplo asignamos a cada celda el valor 0. Como ven debemos usar dos FOR
anidados para recorrer una tabla, uno para recorrer las filas y otro para las columnas.
Vean que el subrango en arr1 indica cuántas columnas tendrá la matriz y en subrango en Tabla indica
cuántas filas.

Una sintaxis alterna para acceder a una celda de nuestra tabla es la siguiente:

Variable_arreglo[fila][columna]

y por tanto si, usando esta sintaxis, quisiéramos acceder a la fila 7 y la columna 4 deberíamos escribir matriz[7]
[4]. No recomiendo esta sintaxis.

Veamos ahora la segunda forma de declarar un arreglo bidimensional, la cual es la que yo recomiendo que
usen:

1 PROGRAM BIDIMENSIONAL;
2
3 Type
4 Tabla= array[1..7,1..5] of integer;
5
6 Var
7 i, j: integer;
8 matriz: Tabla;
9
10 BEGIN
11 For i:= 1 to 7 do
12 For j:= 1 to 5 do
13 matriz[i,j]:= 0;
14 END.

Como ven en la línea 4 hemos declarado el tipo Tabla en el cual hemos incluido dos subrangos entre los
paréntesis rectos separados por coma. El primer subrango (1..7) indica la cantidad de filas y el segundo (1..5) la
cantidad de columnas que tendrá la matriz. Esto siempre es así. Como ven, de este modo queda todo mucho más
claro.
NOTA: La cantidad de celdas de un arreglo bidimensional es igual al resultado de multiplicar la cantidad de
filas por la cantidad de columnas.

Como ejercicio quiero que modifiquen ese programa para que despliegue en la salida estándar la matriz. En
este caso aparecerá una tabla de 0 como esta:

0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

Esto no es muy difícil y radica explícitamente en el uso de FOR.

Ejercicio2: Modifiquen los programas anteriores para que, si el usuario no ingresa un entero, notifiquen el
error y vuelvan a pedir el valor correcto.

Ejercicio3 Se llama matriz cuadrada a toda aquella matriz que tiene tantas filas como columnas, o sea, si tiene
5 filas tiene 5 columnas. La transpuesta de una matriz cuadrada es aquella matriz que tiene como columnas lo que
antes eran filas. O sea, si tenemos una matriz cuadrada A, una matriz cuadrada B es transpuesta de A si las columnas
de B son iguales a las filas de A, o sea, si se satisface que B[i,j]=A[j,i] para todos los valores posibles de i y j.

Vladimir Rodríguez 66
Programación estructurada en Pascal

Ejemplo. Sea A de 5 filas y 5 columnas como se muestra en la figura, veamos como sería su transpuesta B:

A B
1 2 3 4 5 1 0 1 5 2
0 0 0 0 0 2 0 1 4 2
1 1 1 1 1 3 0 1 3 2
5 4 3 2 1 4 0 1 2 2
2 2 2 2 2 5 0 1 1 2

Como ven, lo que en A eran filas, en B son columnas. La primera fila de A que contiene los valores 1, 2, 3, 4
y 5 es la primera columna de B 1, 2, 3, 4 y 5, y así sucesivamente.

Escriban un programa que calcule la transpuesta de un arreglo de números reales con cinco renglones y cinco
columnas. Los valores de la matriz se leen desde la entrada estándar. El resultado se debe imprimir en la salida
estándar. Recuerden la relación B[i,j]=A[j,i] para que sean transpuestas.

Este ejercicio no es muy complicado. Tal vez lo peor de esto es que trabaja con una propiedad matemática de
las matrices y para aquellos que no lo han estudiado puede resultar difícil de comprender. Aún así creo que no
deberían tener problemas.

Arreglos tridimensionales:

Esto es básicamente lo mismo. Se declaran exactamente igual solo que ahora habrá tres subrangos entre
paréntesis rectos y por ende deberemos colocar tres coordenadas para indicar cual celda deseamos ver (una
coordenada para la fila (altura), otra para la columna (largo) y otra para el eje (ancho)). Hago esta aclaración porque
si bien un arreglo bidimensional dibuja una tabla, uno tridimensional dibuja un prisma y por tanto debemos decir
cuan a lo alto, largo y ancho queremos posicionarnos.

Esto es un arreglo bidimensional de 5 filas y 8 columnas. Visto de


este modo es un rectángulo donde la cantidad de filas son su altura y la
cantidad de columnas su largo. Este es un arreglo de 5x8.

Así podríamos visualizar un arreglo


tridimensional de 5x8x6. Como ven es un arreglo de
tablas que estarían todas bien pegadas formando un
prisma. Nosotros debemos indicar en qué fila y
columna está nuestra celda y en cual tabla de todas.
Siempre indicamos [fila, columna, tabla]. Esto es un
intento de que puedan visualizar un arreglo
tridimensional.

El punto rojo correspondería a la posición [4,8,3] por


estar en la cuarta fila, octava columna de la tercera
tabla. El azul correspondería a la posición [1,3,6].

Hasta aquí llegamos con arreglos. Ahora les


propondré un PROYECTO.

Vladimir Rodríguez 67
Programación estructurada en Pascal

REALIZANDO UN PROYECTO

MASTER MIND

Bien, dado todo lo que saben hasta ahora les propongo realizar el siguiente programa. Se trata de un viejo
juego conocido llamado Master Mind. Es un juego para dos personas, una hace de adivinador y la otra de pensador.
El adivinador intentará adivinar un código de cuatro letras, que solo el pensador conoce, en un número máximo de
intentos determinado. Cada vez que el adivinador propone un código, si no adivina, el pensador le dará pistas para
que pueda adivinar. Estas pistas estarán dadas por dos notas, BUENOS y REGULARES. Veamos un ejemplo:

Supongamos que el pensador se inventa el código ABCD para que el otro jugador adivine. Como primer
intento el adivinador presenta el código AFGH. Como ven no ha acertado, pero una de las letras de su código
coincide con la del código del pensador, por tanto hay una bien. Esa A corresponde a un BUENO ya que además de
ser la misma letra está en la misma posición que en el código del pensador, en este caso, en el primer lugar.
Un REGULAR corresponde a una letra que esta en ambos códigos pero en una posición diferente. Por
ejemplo, si para adivinar el ABCD el adivinador presentara el FAGH solo la letra A está en ambos códigos, pero en el
del pensador está en primer lugar y en el del adivinador está en segundo lugar. Ambas notas se presentan a la vez.
Veamos un ejemplo suponiendo que las letras que puede haber en el código van de la A a la F inclusive. Las letras
pueden repetirse.

El código del pensador es FDDA:

Primer intento: ABCD Buenos: 0 Regulares 2


Como ven no hay letras que coincidan en posición, pero tenemos en el código del adivinador una A que está
en el primer lugar (en el del pensador está en el último) y una D en último lugar (en el del pensador está en segundo o
tercer lugar) y corresponden cada una a un regular.

Segundo intento: BADC Buenos: 1 Regulares: 1


Ahora la D coincide en el tercer lugar de ambos códigos. Notar que en el código del pensador hay también
una D en el segundo lugar, pero esta no hace que la D del tercer lugar del código del adivinador sea un regular ya que
esta coincide con la D del tercer lugar. La A siegue estando en posiciones distintas.

Tercer intento: EDAE Buenos: 1 Regulares: 1


Cuarto intento: EDAF Buenos: 1 Regulares: 2
Quinto intento: DDAF Buenos: 1 Regulares: 3
Sexto intento: FADD Buenos: 2 Regulares: 2
Séptimo intento: FDDA Buenos: 4 Regulares: 0

En este caso el adivinador ha ganado. Noten que si suman Buenos + Regulares el resultado siempre será
menor o igual al número de letras en el código (en este caso 4) y sólo será igual si todas las letras en el código del
adivinador están en el del pensador. Nunca puede ser mayor.

Dadas las siguientes declaraciones:

Const
primeraLetra= ‘A’;
ultimaLetra= ‘F’;
largoCodigo= 4;
maxIntentos= 10;

Type
Codigo= array[1..largoCodigo] of char;

deben programar el Master Mind de modo que la CPU haga de pensador y el usuario de adivinador. El programa
deberá crear un código al azar y luego el usuario intentará adivinarlo.
Cada vez que el usuario ingrese un código el programa le dará las notas correspondientes. Si el usuario
adivina el programa debe mostrarle un mensaje de felicitaciones. Si el usuario llega la cantidad máxima de intentos
sin adivinar, el programa debe notificarle que perdió y mostrarle cual era el código que debía adivinar.
Cada vez que el usuario ingrese un caracter que no corresponda al rango de letras del código (‘A’ a ‘F’), o que
ingrese un código más largo o más corto que 4 caracteres el programa debe mostrar un mensaje de error y volver a
pedir el ingreso del código sin aumentar el intento actual del usuario.
En cada momento el programa irá mostrando el intento actual. Veamos unos ejemplos de ejecución:

Dispones de 10 intentos para adivinar:

Vladimir Rodríguez 68
Programación estructurada en Pascal

1)--> ABCD B2 R0
2)--> AFCD B2 R1
3)--> AFD
ERROR
3)--> AFDFF
ERROR
3)--> AFJD
ERROR
3)--> A5FD
ERROR
3)--> ACFD B3 R0
4)--> ACED B4 R0
FELICIDADES. HAS GANADO.

En este ejemplo vemos que el programa primero despliega un mensaje para notificar al usuario de cuantos
intentos máximos dispone para adivinar. Luego comienza el juego. El programa muestra el número de intento actual
y una flecha. Luego se espera la entrada del usuario. El programa despliega las notas y pasa a la línea siguiente
mostrando el intento actual, la flecha y aguardando la entrada del usuario.
También vemos en el tercer intento que el usuario ingresa un código más corto de lo debido, eso es un
ERROR. Muestra el mensaje y pasa a la siguiente línea pero no aumenta el intento actual, seguimos en el tercero.
Luego el usuario ingresa un código más largo de lo debido. Se notifica el error y volvemos a esperar un ingreso sin
aumentar el número de intento. Luego el usuario ingresa un código de cuatro letras pero con una J incluida, la cual no
está en el rango comprendido entre ‘A’ y ‘F’. Esto también es un error. Luego el usuario ingresa un código con un 5 y
este carácter tampoco está en el rango de ‘A’ a ‘F’ y por tanto es un ERROR. Como ven siempre seguimos en el
tercer intento. Finalmente el usuario ingresa bien el código y el programa continúa.
En el cuarto intento el usuario adivina el código, se le informa de ello y así gana.

Veamos un ejemplo en el que pierde:


El código del pensador es ADDB

Dispones de 10 intentos para adivinar:

1)--> ABCD B1 R2
2)--> ABCE B1 R1
3)--> ABCF B1 R1
4)--> AFCD B1 R1
5)--> ABCC B1 R1
6)--> ABBA B1 R1
7)--> ABDB B3 R0
8)--> ADBB B3 R0
9)--> ADCB B3 R0
10)--> ADCB B3 R0
Lamentablemente has perdido.
El código era ADDB.

Crear un código al azar y calcular los Buenos de un código mostrado por el usuario no es muy difícil. La
verdadera dificultad de esto estará a la hora de calcular los Regulares ya que cuando se repitan letras tendrán
problemas para contabilizarlas una única vez. Por ejemplo si el código a adivinar es ADFD y se nos muestra el
ABDB tenemos un Bueno y un Regular. La D está dos veces en el código del pensador y una en el del adivinador,
pero no corresponde a dos regulares, sino a uno.
Otro caso es ADFD y ADBB. En este caso la A está bien y la D coincide en la segunda posición, por tanto es
un Bueno y no un Regular a pesar de que hay una D en la cuarta posición del código del pensador.
Estas y muchas otras dificultades se les presentarán con este programa.

Realmente deben ser capaces de realizarlo. Les llevará como mínimo una semana hacerlo bien, no se maten
por hacerlo en menos tiempo, realmente cuesta. Esta vez tendrán muchas consultas que hacerme, así que escríbanme
a mstrvladi@hotmail.com y pregunten todas las dudas que tengan.
Existen muchísimas formas de realizar este programa. Su imaginación es el límite.
Mucha suerte.

Vladimir Rodríguez 69
Programación estructurada en Pascal

Vladimir Rodríguez 70
Programación estructurada en Pascal

CUARTA PARTE

SUBPROGRAMAS: Procedimientos y Funciones

Vladimir Rodríguez 71
Programación estructurada en Pascal

Introducción:

Bien, hasta ahora el tema más complicado que hemos visto lo representan los arreglos (array); sin embargo
entraremos ahora en temas un tanto más difíciles y que por ende requerirán más trabajo por parte de ustedes a la hora
de practicar. Yo intentaré explicarlos lo más detalladamente posible y de un modo entendible siempre partiendo de la
base de que ustedes como lectores no tienen conocimiento alguno sobre programación.
Hasta el momento hemos visto programas que podían o no estar divididos en varios bloques. Por si no lo
recuerdan llamábamos bloque a aquella sección de código delimitada entre un BEGIN y un END. Cada bloque
siempre contiene más de una instrucción, de lo contrario no tendría sentido definirlo aunque es posible hacerlo, pero
es visto por el programa como una única instrucción.
Del modo en que hemos venido programando, si nuestro programa tuviera que realizar la misma tarea más de
una vez nosotros deberíamos escribir las mismas líneas de código todas esas veces. Esto extiende muchísimo nuestro
código fuente dejándolo difícil de entender además de complicar muchísimo la tarea del programador. Es por esto
que existen dos estructuras muy importantes y potentes que forman a los llamados subprogramas (pequeños
programas dentro del programa principal) conocidos como PROCEDIMIENTOS y FUNCIONES (en otros lenguajes,
como Java, ambas estructuras se conocen como Métodos).
Estas estructuras comienzan con lo que en programación se conoce como Modularización ya que como su
nombre lo indica permiten formar módulos de programa que se encargan de ciertas tareas específicas. De este modo
el código queda mucho más entendible y compacto además de lograrse una buena ordenación del mismo. Es
sumamente importante que entiendan todo este capítulo antes de continuar si en verdad quieren lograr crear un buen
programa, sea del tipo que sea, ya que los procedimientos y las funciones son básicos para cualquier programador
avanzado.

Vladimir Rodríguez 72
Programación estructurada en Pascal

Procedimientos:

Declaración y llamado:

Veamos primero los PROCEDIMIENTOS: Un Procedimiento será un subprograma que se encargará de


realizar una tarea específica dentro del programa principal. Al igual que al programa principal, a los procedimientos
se les asigna un nombre (identificador) con el cual podremos llamarlo luego para pedirle que realice su tarea. Un
procedimiento se define de la siguiente manera

PROCEDURE identificador(parámetros);

Usamos la palabra reservada PROCEDURE para indicar que definiremos un procedimiento, luego le damos un
nombre cualquiera que identifique su tarea o que nos sirva a nosotros como referencia. Seguido del nombre se
colocan entre paréntesis los parámetros que utilizará el procedimiento para trabajar (esto lo veremos dentro de poco)
y finalmente un punto y coma. Todo esto compone lo que llamamos encabezado o firma del procedimiento. No
pueden existir dos procedimientos con el mismo identificador ya que como ha sido hasta ahora y debería resultarles
obvio, no pueden repetirse identificadores. Esto puede cambiar de un lenguaje a otro, pero no entraremos en esos
detalles, concentrémonos en Pascal.

Como ya he dicho, un procedimiento es un subprograma, o sea, un pequeño programa dentro del programa
principal y por este motivo tendrá una estructura casi idéntica a la de este último. Lo que quiero decir con esto es que,
luego del encabezado del procedimiento, podremos definir CONSTANTES, TIPOS, VARIABLES e incluso otros
subprogramas dentro. También tendrá un bloque principal delimitado entre BEGIN y END solo que este END
terminará en punto y coma. Nunca olviden que el único END que termina en punto es el del programa principal.

Esta sería la estructura del programa principal con lo visto hasta el momento:

PROGRAM Nombre_del_programa;
Const //Declaramos constantes.
Type //Declaramos tipos.
Var //Declaramos variables.
BEGIN
//Bloque principal del programa.
END.

Veamos la estructura pero agregando el lugar donde se declaran los procedimientos y funciones:

PROGRAM Nombre_del_programa;
Const //Declaramos constantes.
Type //Declaramos tipos.
Var //Declaramos variables.
{Declaración de Procedimientos y Funciones}
BEGIN
//Bloque principal del programa.
END.

Veamos ahora la estructura de un procedimiento:

PROCEDURE Nombre_del_procedimiento(parámetros);
Const //Declaración de constantes.
Type //Declaración de tipos.
Var //Declaración de variables.
BEGIN
{Bloque principal del procedimiento}
END;

Noten que la estructura es idéntica a la del programa principal excepto por el encabezado y por el hecho de
que el END del bloque principal termina en punto y coma. A esa estructura debería añadirle la declaración de
funciones y procedimientos, pero esta va en el mismo lugar que en el bloque del programa principal y no creo que
haga falta mostrarles donde va. No se pongan nerviosos, pronto entenderán mejor todo esto.

Veamos un ejemplo sencillo en el que pedimos al usuario que ingrese su Nombre, su Apellido, su Edad, su
Documento de Identidad y su Dirección. Luego mostraremos los datos ingresados pero separados por una línea
formada por guiones y luego varias líneas en blanco. Veremos la versión sin procedimiento y luego la versión con
procedimiento.

Vladimir Rodríguez 73
Programación estructurada en Pascal

Para este ejemplo en particular usaremos el tipo primitivo STRING, que lee cadenas de caracteres desde la
entrada estándar. Lo correcto a los efectos de este manual sería leer carácter a carácter y guardar estos en un arreglo
para formar la palabra, pero esto se los pediré luego en un ejercicio.

1 PROGRAM Datos_de_Usuario;
2
3 Const
4 separador='-';
5 lineas_a_saltear= 4;
6
7 Var
8 nombre, apellido, documento, direccion: string;
9 i: integer;
10
11 BEGIN
12 //Pedimos los ingresos al usuario.
13 Write('Ingresa tu nombre: ');
14 readln(nombre);
15 write('Ingresa tu apellido: ');
16 readln(apellido);
17 write('Ingresa tu documento: ');
18 readln(documento);
19 write('Ingresa tu dirección: ');
20 readln(direccion);
21
22 //Realizamos la primera separación.
23 For i:=1 to 20 do
24 write(separador);
25 For i:=1 to lineas_a_saltear do
26 writeln;
27
28 //Escribimos el nombre del usuario.
29 writeln('NOMBRE: ',nombre);
30
31 //Realizamos la segunda separación.
32 For i:=1 to 20 do
33 write(separador);
34 For i:=1 to lineas_a_saltear do
35 writeln;
36
37 //Escribimos el apellido del usuario.
38 writeln('APELLIDO: ',apellido);
39
40 //Realizamos la tercera separación.
41 For i:=1 to 20 do
42 write(separador);
43 For i:=1 to lineas_a_saltear do
44 writeln;
45
46 //Escribimos el documento del usuario.
47 writeln('DOCUMENTO: ',documento);
48
49 //Realizamos la cuarta separación.
50 For i:=1 to 20 do
51 write(separador);
52 For i:=1 to lineas_a_saltear do
53 writeln;
54
55 //Escribimos la dirección del usuario.
56 writeln('DIRECCIÓN: ',direccion);
57 END.

Este es un programa muy sencillo y no deberían tener ninguna dificultad al leerlo. Aún así explicaré lo que
hace así luego se entiende perfectamente el ejemplo con procedimiento.
Declaramos en las primeras líneas dos constantes: separador, que es del tipo char inicializada con el
guión; este carácter será el que dibuje una línea separadora, esto es bien simple, escribimos unos 20 guiones en la
misma línea y ya. Luego está la constante lineas_a_saltear que es del tipo integer inicializada con el número 4
y es la que utilizaremos para indicar cuantas líneas en blanco dejaremos entre dato y dato.

Vladimir Rodríguez 74
Programación estructurada en Pascal

Luego declaramos cuatro variables del tipo string para leer los datos del usuario, una por cada dato. Como ya
dije, el tipo string lee los datos en forma de cadenas de caracteres. Esto implica que si uno ingresa un valor 4556 no
será el número entero 4556 sino la cadena de caracteres ‘4556’. Es ideal para leer palabras o frases, pero no
hablaremos de su uso en este manual ya que la idea aquí es que ustedes aprendan a programar estructuradamente y
entiendan como funcionan las herramientas más primitivas. De este modo lograrán desarrollar una gran habilidad
para programar y serán capaces de resolver grandes problemas con pocas herramientas, que muchas veces será a lo
que tendrán que enfrentarse.
La otra variable declarada es i, del tipo entero, y será la que usaremos como variable de control para los FOR.
Desde la línea 13 a 20 inclusive solo pedimos al usuario que ingrese sus datos y los leemos desde la entrada
estándar. Esto no implica ninguna dificultad para ustedes.
En la línea 23 declaramos un FOR que simplemente dibuja los 20 guiones y en la línea 25 declaramos el FOR
que se encarga de dejar líneas en blanco, tantas como la constante lineas_a_saltear indique en su valor. Estos
dos FOR se repetirán varias veces durante el código, cada vez que necesitemos dibujar la línea de guiones y dejar
líneas en blanco.
Como podrán ver, usaremos cuatro veces estas dos instrucciones en nuestro programa. Realmente esto resulta
estúpido ya que quedaría más prolijo si no dejáramos líneas en blanco para presentar los datos del usuario, pero la
intención de este ejemplo es ver la necesidad de los procedimientos de una manera sencilla, ya que un buen ejemplo
sería muy complejo y no es adecuado presentarlo en el momento en el que ustedes están intentando aprender una
nueva herramienta y que resultará bastante difícil de comprender si no se trabaja con cuidado.

Ahora veamos el mismo ejemplo pero con un procedimiento llamado Saltear que se encargue de realizar la
tarea de los dos FOR, o sea, dibujar la línea de guiones y de dejar líneas en blanco:

1 PROGRAM Datos_de_Usuario;
2
3 Const
4 separador='-';
5 lineas_a_saltear= 4;
6
7 Var
8 nombre, apellido, documento, direccion: string;
9 i: integer;
10
11 Procedure Saltear();
12 Begin
13 For i:= 1 to 20 do
14 Write(separador);
15 For i:= 1 to lineas_a_saltear do
16 Writeln;
17 End;
18
19 BEGIN
20 //Pedimos los ingresos al usuario.
21 Write('Ingresa tu nombre: ');
22 readln(nombre);
23 write('Ingresa tu apellido: ');
24 readln(apellido);
25 write('Ingresa tu documento: ');
26 readln(documento);
27 write('Ingresa tu dirección: ');
28 readln(direccion);
29
30 //Realizamos la primera separación.
31 Saltear();
32 //Escribimos el nombre del usuario.
33 writeln('NOMBRE: ',nombre);
34
35 //Realizamos la segunda separación.
36 Saltear();
37
38 //Escribimos el apellido del usuario.
39 writeln('APELLIDO: ',apellido);
40
41 //Realizamos la tercera separación.
42 Saltear();
43
44 //Escribimos el documento del usuario.
45 writeln('DOCUMENTO: ',documento);

Vladimir Rodríguez 75
Programación estructurada en Pascal

46
47 //Realizamos la cuarta separación.
48 Saltear();
49
50 //Escribimos la dirección del usuario.
51 writeln('DIRECCIÓN: ',direccion);
52 END.

Bien, este programa es idéntico al anterior en el sentido de que realiza exactamente la misma tarea. Como
sucedía en la versión anterior, repetíamos cuatro veces la declaración de los FOR que se ocupan de dibujar los
guiones y saltear líneas. Ahora lo haremos solo una vez dentro del procedimiento Saltear.
Las declaraciones de constantes y variables quedan idénticas a las anteriores. Ahora la diferencia principal
está en que luego de las variables declaramos a nuestro procedimiento. Veámoslo detalladamente:

Procedure Saltear();
Begin
For i:= 1 to 20 do
Write(separador);
For i:= 1 to lineas_a_saltear do
Writeln;
End;

Tenemos su encabezado que consta simplemente de la palabra reservada procedure y luego de su


identificador Saltear seguido por los paréntesis. En este caso estos paréntesis no tienen nada dentro porque este
procedimiento no utiliza parámetros, eso lo veremos más adelante. A pesar de que no existan parámetros, es
conveniente incluir los paréntesis vacíos, aunque pueden no hacerlo si lo prefieren.
Luego del encabezado podríamos haber declarado constantes, variables, tipos y/u otros procedimientos, pero
no hace falta para este ejemplo tan sencillo, así que procedemos directamente a iniciar el bloque principal con la
palabra Begin. A partir de aquí se escriben todas las instrucciones que realizará el procedimiento, las cuales pueden
ser lo que a ustedes se les ocurra, lo que les haga falta y que saben utilizarán en más de una ocasión. No olviden que
un procedimiento es un pequeño programa dentro del programa principal y por lo tanto admite exactamente las
mismas reglas.
En este caso nuestro procedimiento contiene a los dos FOR de antes y nada más. Cerramos su bloque
principal con End; con lo cual nuestro programa sabe que allí termina la declaración de Saltear.
Bien, ya tenemos nuestro procedimiento que se encarga de dibujar la línea de guiones y de dejar líneas en
blanco, ahora solo debemos llamarlo cada vez que lo necesitemos. Esto es bien fácil, solo escribimos su nombre en el
lugar en el que deseamos que realice su tarea y entre paréntesis colocamos los parámetros que utilizaremos, en este
caso como no hay parámetros solo dejamos los paréntesis vacíos aunque bien podríamos no incluirlos. En este
ejemplo vemos las cuatro llamadas en las líneas 31, 36, 42 y 48.
En este caso en particular no ahorramos muchas líneas de código al utilizar el procedimiento, pero como ya
dije, un verdadero ejemplo sería muy complejo y en realidad los procedimientos y funciones utilizados llegan a estar
formados por cientos y cientos de líneas. Como ven, utilizando un procedimiento solo hace falta escribir una única
vez una tarea específica volviéndose el código más legible y más fácil de mantener en caso de errores o actualización.
Cuando nuestro programa principal llega, por ejemplo, a la línea 31, regresa a la 12 (o a aquella donde esté el
BEGIN del bloque principal del procedimiento) y realiza una por una las instrucciones allí indicadas hasta llegar al
END que finaliza el bloque del procedimiento. Pueden ver esto si utilizan el DEBUGER paso a paso utilizando F7 en
vez de F8 para avanzar. Como ya expliqué antes, F7 ingresa dentro de los subrprogramas en el paso a paso y F8 no.

Variables Globales y Locales. Introducción al Alcance de Identificadores:

En nuestro ejemplo vimos que nuestro procedimiento no poseía ninguna variable declarada dentro. Sin
embargo también vemos que la única variable que usa es i y la única constante es lineas_a_saltear además de
que ambas solo son usadas por el procedimiento y por nadie más. Dado este caso bien podríamos haberlas declarado
adentro del procedimiento para que se entienda que solo serán usadas por este y por nadie más.
Las variables declaradas en el programa principal son llamadas variables globales y las que se declaran
dentro de un procedimiento o una función son llamadas variables locales o internas. Las variables globales son
visibles dentro de todo el programa, o sea, uno puede hacer referencia a ellas en cualquier parte del código. Por
ejemplo, si tenemos una variable global entera llamada x podremos asignarle un valor en cualquier parte de nuestro
programa, o sea, escribir x:=10 en la línea número 5 y luego en la línea número 1258 escribir x:= 25 o utilizarla
en expresiones o para que el usuario ingrese un valor, etc., por citar ejemplos tontos. Las variables locales son
visibles solo dentro del procedimiento o la función en que son declaradas. Esto implica que solo podamos hacer
referencia a variables locales dentro del bloque principal de su subprograma y nó fuera. Por ejemplo, si tenemos una
variable string llamada nombre dentro de un procedimiento podremos utilizarla como queramos solo dentro su
bloque principal, por ejemplo con una instrucción readln(nombre), pero si escribimos la misma instrucción u

Vladimir Rodríguez 76
Programación estructurada en Pascal

otra que implique a dicha variable fuera del bloque principal de su subprograma el compilador no la reconocerá
porque no la ve y nos dirá que no está declarada.
Si vemos al programa principal como una máquina, por ejemplo, un vehículo, cada subprograma
corresponderá a alguna parte que se encarga de una tarea específica. Por ejemplo, nosotros podemos conducir y
utilizar un reproductor de CDs a la vez. La unidad lectora de CDs del vehículo es como un procedimiento que se
encarga de reproducir los CDs cuando nosotros lo dispongamos, pero aunque la podemos utilizar cuando queramos y
colocar el CD que queramos (este sería nuestro parámetro), no podemos ver ni utilizar sus piezas internas (que serían
sus variables locales). De este modo si se rompe la unidad reproductora simplemente debemos ir directamente a ella
para repararla sin necesidad de tocar nada más del vehículo. Lo mismo sucede con nuestros subprogramas.

1 PROGRAM Datos_de_Usuario;
2
3 Var
4 nombre, apellido, documento, direccion: string;
5
6 Procedure Saltear();
7 Const
8 separador='-';
9 lineas_a_saltear= 4;
10
11 Var
12 i: integer;
13 Begin
14 For i:= 1 to 20 do
15 Write(separador);
16 For i:= 1 to lineas_a_saltear do
17 Writeln;
18 End;
19
20 BEGIN
21 //Pedimos los ingresos al usuario.
22 Write('Ingresa tu nombre: ');
23 readln(nombre);
24 write('Ingresa tu apellido: ');
25 readln(apellido);
26 write('Ingresa tu documento: ');
27 readln(documento);
28 write('Ingresa tu dirección: ');
29 readln(direccion);
30
31 //Realizamos la primera separación.
32 Saltear();
33 //Escribimos el nombre del usuario.
34 writeln('NOMBRE: ',nombre);
35
36 //Realizamos la segunda separación.
37 Saltear();
38
39 //Escribimos el apellido del usuario.
40 writeln('APELLIDO: ',apellido);
41
42 //Realizamos la tercera separación.
43 Saltear();
44
45 //Escribimos el documento del usuario.
46 writeln('DOCUMENTO: ',documento);
47
48 //Realizamos la cuarta separación.
49 Saltear();
50
51 //Escribimos la dirección del usuario.
52 writeln('DIRECCIÓN: ',direccion);
53 END.

No solo las variables son visibles según donde están declaradas, las constantes y los tipos también pueden ser
globales o locales.
Como vemos en este ejemplo, he colocado las constantes de nuestro programa y la variable i dentro del
procedimiento Saltear. Si ustedes quisieran referirse a i fuera del procedimiento tendrían un error en tiempo de

Vladimir Rodríguez 77
Programación estructurada en Pascal

compilación indicándoles que no encuentra el identificador “i”. Esto es porque i no es visible fuera del
procedimiento. Es como una pieza de nuestro reproductor de CDs, no podemos verlas ni utilizarlas. Esto garantiza
que el procedimiento se dedique únicamente a su tarea y esta no pueda ser modificada accidentalmente por un cambio
de valores o cosas por el estilo.
Fíjense que si tuviéramos un programa de miles y miles de líneas donde solo existen variables globales,
cuando nuestros subprogramas se dispongan a usarlas, si nosotros no nos dimos cuenta y las modificamos antes en
algún lado del programa, realizarán erróneamente sus tareas además de que son totalmente dependientes de lo que
sucede globalmente en el programa y por tanto de nuestra suma atención en el momento de programar. Esto causa
que el intentar corregir errores se vuelva una tarea ardua y tediosa cuando en realidad podría ser más sencillo. En
nuestro vehículo no sería aconsejable que funcionara mal el reproductor de CDs porque ser rompió el radiador, por
ejemplo, sino que el reproductor es independiente y por ende no depende de las demás piezas del vehículo. A la
inversa, no sería bueno que no funcionara el radiador si se nos rompe el reproductor. Esto se aplica a otras
muchísimas partes, por supuesto.

Ahora bien, no olvidemos que las variables globales son visibles en todo el programa y podemos usarlas tanto
dentro como fuera de los subprogramas. En nuestro vehículo podríamos decir que la batería es global ya que es usada
por muchas partes y si esta falla todas esas partes fallarán. En realidad lo correcto sería decir que el sistema eléctrico
es un subprograma donde está declarada la batería y dentro de él tenemos a otros subprogramas como el reproductor
de CDs, el claxon, las luces, los limpiaparabrisas, etc, y por tanto para todos estos subprogramas la batería resultaría
global, aunque no para el resto del vehículo.

Dicho de otra manera. Todo lo declarado dentro de un subprograma es local al mismo y por ende solo visible
dentro de este, pero resulta global a lo que está declarado allí dentro. Siendo así, si tenemos un subprograma dentro
del cual hay más subprogramas, las variables declaradas dentro de él serán “globales” a sus subprogramas pero no
serán visibles fuera de él.
Veamos esto con declaraciones sencillas:

PROGRAM alcance_identificadores;

Var
x, y: integer;

Procedure Procedimiento1();
Var
a, b: integer;

Procedure Procedmiento2();
Var
j, k: integer;
Begin
//Instrucciones Procedimiento2.
End;
Begin
//Instrucciones Procedimiento1.
End;

BEGIN
//Instrucciones programa principal.
END.

Este es un ejemplo genérico sencillo. Tenemos el programa principal y dos procedimientos,


Procedimiento1 y Procedmiento2 de modo que el segundo está declarado dentro del primero. Están las
variables globales x e y declaradas en el programa principal y por ende visibles en cualquier parte del código, dentro
o fuera de cualquiera de los procedimientos. Declaradas en Procedimiento1 están las variables a y b las cuales
son visibles solo dentro de dicho procedimiento y no fuera. Como Procedmiento2 está declarado dentro de
Procedimiento1 estas variables resultan globales para Procedmiento2, o sea que son visibles dentro de todo
su código. Finalmente dentro de Procedmiento2 están declaradas j y k las cuales son solo visibles allí dentro y
en ningún otro lado.
Ahora veamos este mismo ejemplo pero con un funcionamiento sencillo:

1 PROGRAM alcance_identificadores;
2
3 Var
4 x, y: integer;
5
6 Procedure Procedimiento1();
7 Var

Vladimir Rodríguez 78
Programación estructurada en Pascal

8 a, b: integer;
9
10 Procedure Procedmiento2();
11 Var
12 j, k: integer;
13 Begin
14 x:= 10;
15 y:= 11;
16 a:= 1;
17 b:= 2;
18 j:= 20;
19 k:= 21;
20
21 write(x,' ',y,' ',a,' ',b,' ',j,' ',k);
22 End;
23 Begin
24 Procedimiento2();
25 End;
26
27 BEGIN
28 Procedimiento1();
29 END.

Como ya deberían saber, dadas estas declaraciones, cualquier variable es visible en Procedimiento2 así
que utilizamos este para inicializarlas todas con los valores que ven y luego utilizamos el método WRITE para
mostrar estos números en pantalla separados por un espacio. Procedimiento1 lo único que hace es llamar a
Procedimiento2 y el programa principal lo único que hace es llamar a Procedimiento1. Este programa
podría resumirse solo a tener Procedimiento2 o incluso ni tenerlo, pero es para que aprendan esto del alcance de
los identificadores de las variables.
Ahora bien. ¿Qué pasa si escribimos la instrucción de la línea 21 fuera de Procedimiento2? Si lo
hacemos dentro Procedimiento1 tendríamos un error de compilación ya que dentro del WRITE hacemos
referencia a las variables j y k y estas solo son visibles dentro de Procedimiento2. Si lo hacemos fuera de
Procedimiento1 tendríamos un error del mismo tipo al anterior ya que hacemos referencia a las variables a, b,
j y k que no son visibles fuera de sus procedimientos. Por este motivo el único que puede mostrar en pantalla los
resultados es Procedimiento2 ya que es donde son visibles todas las variables de nuestro programa.
No hace falta inicializar todas las variables dentro de Procedimiento2. Veamos el mismo ejemplo pero
inicializando cada variable dentro de su procedimiento para luego mostrar el resultado en pantalla:

1 PROGRAM alcance_identificadores;
2
3 Var
4 x, y: integer;
5
6 Procedure Procedimiento1();
7 Var
8 a, b: integer;
9
10 Procedure Procedmiento2();
11 Var
12 j, k: integer;
13 Begin
14 j:= 20;
15 k:= 21;
16
17 write(x,' ',y,' ',a,' ',b,' ',j,' ',k);
18 End;
19 Begin
20 a:= 1;
21 b:= 2;
22 Procedimiento2();
23 End;
24
25 BEGIN
26 x:= 10;
27 y:= 11;
28 Procedimiento1();
29 END.

Vladimir Rodríguez 79
Programación estructurada en Pascal

Este programa produce exactamente la misma salida que el anterior. Solo hemos inicializado las variables en
distintos lugares. Deben identificar bien cada bloque, o sea, cual pertenece al programa principal, cual a un
procedimiento y cual al otro.

Si no logran entender esto no deben continuar hasta que lo hayan logrado ya que esto forma parte de la base
necesaria para entender subprogramas. Lo que viene a continuación requiere que hayan comprendido donde puede
visualizarse cada variable en dependencia de donde ha sido declarada.

Sombreado de Identificadores:

Bien, continuemos con esto del alcance de los identificadores. Hasta ahora simplemente hemos visto que cada
variable es visible dentro del cuerpo del subprograma en que ha sido declarada y no fuera. Ahora bien, la
complicación con esto comienza cuando tenemos una variable global con cierto nombre y luego en un subprograma
tenemos una variable local con el mismo nombre. ¿Pero como? ¿No era que no pueden repetirse identificadores? En
efecto, no pueden repetirse, pero como hemos visto, una variable local es visible solo dentro del subprograma donde
se ha declarado y por tanto fuera de este es como si no estuviera declarada, como si no existiera, por lo tanto es
posible declarar otra con el mismo nombre.
Pero si la variable global es visible dentro de un subprograma, cuando además declaramos dentro de este una
variable local con el mismo nombre ¿cómo sabemos a cual nos estamos refiriendo dentro del subprograma?

Esto es simple pero genera muchas confusiones. Si tenemos una variable global con un nombre idéntico al de
una local, cuando dentro del subprograma nos referimos a ese identificador tiene precedencia la variable local, o sea,
esta “sombrea” a la global dentro del subprograma. Veamos un ejemplo bien sencillo de entender:
1 PROGRAM sombreo_identificadores;
2
3 Var
4 X: integer;
5
6 Procedure sombreo();
7 Var
8 X: integer;
9
10 Begin
11 X:= 10;
12 End;
13
14 BEGIN
15 Sombreo();
16 END.

En este ejemplo tenemos una variable global X y una local dentro del procedimiento sombreo también
llamada X. Como vemos, dentro del procedimiento he inicializado X en 10. El programa principal lo único que hace
es llamar al procedimiento.
¿Cuál de las dos X vale 10? Como la inicialización fue dictada dentro del procedimiento, al referenciar a X
nos estamos dirigiendo a la X local y no a la global, por lo tanto la X que vale 10 es la interna al procedimiento y la
otra no sabemos cuanto vale porque no le hemos dado ningún valor aún.
Ahora veamos otro ejemplito sencillo:
1 PROGRAM sombreo_identificadores;
2
3 Var
4 X: integer;
5
6 Procedure sombreo();
7 Var
8 X: integer;
9
10 Begin
11 X:= 10;
12 Writeln(X);
13 End;
14
15 BEGIN
16 X:= 5;
17 Sombreo();

Vladimir Rodríguez 80
Programación estructurada en Pascal

18 Write(X);
19 END.
Ahora he inicializado ambas variables, la global con el valor 5 y la interna con el valor 10. ¿Cómo será la
salida de este programa?

Parámetros: Pasaje por Valor:

Con lo visto hasta ahora acerca de los procedimientos no podemos hacer mucho ya que no resultan demasiado
útiles. En la mayoría de los casos nuestro programa irá realizando tareas y obteniendo resultados con dichas tareas, y
muchas veces necesitaremos pasarle esos datos a los procedimientos para que trabajen con ellos. Si volvemos a
nuestro ejemplo del vehículo y el reproductor de CDs, nosotros necesitamos introducir un CD para que este sea
reproducido obteniendo como resultado la música.
Esto puede hacerse con las variables globales, pero como ya dije, lo correcto es hacer que el procedimiento
sea independiente al resto del programa. Es por eso que existen los parámetros, para introducir datos al procedimiento
y eventualmente para obtenerlos.

Los parámetros no son más que variables como las que ya conocemos y que funcionarán como variables
locales al procedimiento, con dos pequeñas diferencias: se declaran en el encabezado y no dentro del cuerpo del
procedimiento; y se inicializan al entrar al procedimiento con los valores que nosotros pasemos como parámetros.
Veamos esto en un ejemplo sencillo:

1. PROGRAM triangulo;
2.
3. Var
4. base, altura: real;
5.
6. Procedure calcularArea(b, h: real);
7. Begin
8. if (b>0) and (h>0) then
9. writeln('El área del triángulo es: ',(b*h)/2:1:1)
10. else
11. writeln('ERROR. Ninguna dimensión de un triángulo puede ser
menor o igual a 0.');
12. End;
13.
14. BEGIN
15. //Pedimos el ingreso de datos al usuario.
16. write('Ingaresa el valor de la base: ');
17. readln(base);
18. write('Ingresa el valor de la altura: ');
19. readln(altura);
20.
21. //Llamamos al procedimiento calcularArea.
22. calcularArea(base,altura);
23. END.

Este es un programa sencillo para calcular el área de un triángulo, no muy diferente al que ya hemos creado.
Tenemos dos variables reales globales, una para el valor de la longitud de la base del triángulo y otra para el valor de
la longitud de la altura. Desde la línea 15 hasta la 19 inclusive pedimos al usuario que ingrese los valores de estas
dimensiones.
Veamos el encabezado del procedimiento calcularArea. Entre paréntesis tenemos la declaración de dos
variables reales, b y h. Como ven solo se trata de una declaración como cualquier otra, solo que no lleva la palabra
VAR delante (luego veremos que sí puede llevarla pero solo para casos especiales). Esas variables, b y h,
corresponden a los parámetros del procedimiento, en este caso dos, por lo tanto cuando lo llamemos debemos
ingresarle dos valores para sus parámetros para que las variables b y h los tomen.
Vean la llamada de la línea 22: calcularArea(base,altura); Es igual a las anteriormente vistas con
la diferencia de que ahora entre paréntesis hemos colocado los valores de los parámetros, los cuales, en este caso,
están dados por las variables base y altura.
El orden en el que se ingresan los parámetros en la llamada al procedimiento es el orden en el que se tomarán,
por lo tanto base, que está en primer lugar, le dará su valor a b ya que es el primer parámetro declarado; y altura

Vladimir Rodríguez 81
Programación estructurada en Pascal

le dará su valor a h ya que fue declarado en segundo lugar. Los parámetros se pasan separados por comas tal y como
lo ven ustedes allí.

También es posible pasar directamente valores como parámetros al procedimiento, por ejemplo, hacer una
llamada de este estilo: calcularArea(10,20.33). Si ese fuera el caso b tomaría el valor 10 y h el 20.33. Es
por esto que hoy dije que los parámetros se inicializan en la llamada al procedimiento, ya que siempre tomarán los
valores que se les pasen.
No es posible pasar un número de parámetros distintos al declarado en el encabezado. Por ejemplo,
calcularArea tiene dos parámetros y por tanto solo dos valores deben pasarse en su llamada, ni más ni menos.

Otra cosa muy pero muy importante es que sean compatibles los tipos de parámetros con los valores que se les
pasan. En este caso los parámetros son ambos del tipo real y por tanto esperarán valores reales o enteros en la
llamada, pero no podrán ir caracteres o cadenas de caracteres. Si lo hacen tendrán un error en tiempo de compilación
ya que el compilador se dará cuenta de que han introducido tipos incompatibles.

Declaración de parámetros de tipos diferentes:

El encabezado del procedimiento anterior era


Procedure calcularArea(b, h: real);
pero bien podía haber sido
Procedure calcularArea(b: real; h: real);

Es posible declarar los parámetros por separado indicando su tipo y utilizando el punto y coma para comenzar
con la declaración siguiente. Sin embargo resulta más útil declarar todos los parámetros de un mismo tipo
especificando solo una vez dicho tipo, tal y como lo hice en el ejemplo del triángulo. Ahora veamos ejemplos de
encabezados sencillos donde hay parámetros de tipos distintos:

Procedure procedimiento1(a, b: real; x, y: integer; letra: char;


nombre: string);

Como ven tenemos seis parámetros, dos reales, dos enteros, un carácter y una cadena. Ese orden de
declaración deberá respetarse para llamar al procedimiento:
procedimiento1(un real, otro real, un entero, otro entero,
un carácter, una cadena);
Si cambian dicho orden tendrán un error de compilación.

Otro ejemplo:

Procedure procedimiento2(a, b: integer; n: char; x: integer);

Aquí hay cuatro parámetros, dos enteros, un carácter y otro entero. ¿Por qué no declaré todos los enteros en una sola
declaración? O sea, este encabezado bien podría ser

Procedure procedimiento2(a, b, x: integer; n: char);

pero aunque son los mismos parámetros el orden está en lugares diferentes y por tanto los valores se pasarán en dicho
orden. En el primer ejemplo la llamada sería así:

Procedimiento2(un entero, otro entero, un carácter, un entero);

y en el segundo ejemplo sería así:

Procedimiento2(un entero, otro entero, un entero, un carácter);

Tengan mucho cuidado con esto ya que el orden de declaración y el de invocación deben ser iguales. A veces
por una cuestión de entendimiento es mejor utilizar un encabezado como el del primer ejemplo. Veamos algo tonto
pero tal vez les ayude a entender esto: Supongamos que tenemos un procedimiento del cual no nos interesa su
funcionamiento pero toma como parámetros el nombre de dos usuarios y sus edades. Un encabezado podría ser este:

Procedure usuarios(nombre1, nombre2: string; edad1, edad2: real);

Otro podría ser este:

Vladimir Rodríguez 82
Programación estructurada en Pascal

Procedure usuarios(nombre1: string; edad1: real; nombre2: string;edad2: real);

Dados los nombres de los parámetros en ambos se entiende que nombre1 y edad1 corresponden a un
usuario y nombre2 y edad2 a otro. Sin embargo en la llamada, en el primer ejemplo introducimos los dos nombres
y luego las dos edades en el orden correspondiente (usuario1, usuario2, edad de usuario1, edad de usuario2) mientras
que en el segundo ejemplo introducimos el usuario, su edad, el otro usuario y su edad. Les presento ambos modelos
para que los tengan en mente. Ustedes seleccionarán el más adecuado o crearán los suyos propios según lo vean
necesario.

Es importante que sepan que los parámetros pueden ser de cualquier tipo, incluso los declarados por ustedes
mismos siempre que dichos tipos sean globales para el subprograma en cuestión.

Asignaciones de parámetros:

Como ya dije antes, los parámetros funcionan como variables locales al procedimiento al que pertenecen y
por lo tanto pueden usarse como tales. O sea, podemos asignarle valores a un parámetro, utilizarlo en una expresión,
hacer READ y WRITE, etc. No creo que esto requiera de ejemplos ya que ustedes deben trabajar muy bien con las
variables a estas alturas.

Parámetros Nominales y parámetros Efectivos:

Llamamos parámetros nominales a los nombres (identificadores) que aparecen en el encabezado del
procedimiento o función y parámetros efectivos a los que usamos en la llamada al subprograma, ya sean valores,
variables o expresiones.
En nuestro ejemplo del triángulo los parámetros nominales eran b y h, y los efectivos eran base y altura.

Hasta ahora hemos visto el método de pasaje de parámetros por valor, o sea, nosotros le pasamos valores a los
parámetros en la llamada al procedimiento y sus parámetros se inicializan con dichos valores para trabajar. Lo que
sucede luego con los parámetros no influye en el resto del programa, solo en el interior del procedimiento, excepto
claro que modifiquemos a propósito variables globales, cosa que no recomiendo. Entonces, si dices que no es
recomendable modificar variables globales, ¿cómo podemos obtener resultados de un procedimiento?
Esa sería una buena pregunta. Si luego de calcular el área de triángulo quisiéramos seguir trabajando con ese
valor, pero no queremos utilizar variables globales ¿qué hacemos?
Allí es donde entra el pasaje de parámetros por referencia, pero no hablaré de ello todavía, ya que es lo más
complicado de este tema.

En vez de eso hablemos primero de las funciones.

Vladimir Rodríguez 83
Programación estructurada en Pascal

Funciones:

Declaración y llamado:

Las funciones no difieren casi en nada de los procedimientos. Tienen solo dos diferencias fundamentales: el
encabezado, y que las funciones devuelven un valor como resultado de su trabajo. De este modo, para calcular el área
de un triángulo lo correcto es usar una función y no un procedimiento.
Grábense esto bien:

LAS FUNCIONES DEVUELVEN VALORES COMO RESULTADO, LOS


PROCEDIMIENTOS NO.

Veamos como se declara una función:

Function nombre(parámetros): tipo;

En vez de utilizar la palabra reservada Procedure utilizamos Function. Luego le damos un nombre y
entre paréntesis declaramos sus parámetros tal y como lo hacíamos con los procedimientos. Luego de la declaración
de parámetros van dos puntos y el tipo de valor que devolverá la función. Este tipo debe ser un tipo simple, o sea, no
puede ser enumerado, arreglo (array), conjunto (set) ni registro (record). Todavía no hemos hablado de algunos de
ellos, pero ya lo haremos luego.

Ahora veamos el ejemplo del área del triángulo utilizando una función que la calcule y devuelva su valor:
1. PROGRAM triangulo;
2.
3. Var
4. base, altura, area: real;
5.
6. Function calcularArea(b, h: real): real;
7. Begin
8. if(b>0) and (h>0) then
9. calcularArea:= b*h/2
10. else
11. calcularArea:= 0;
12. End;
13.
14. BEGIN
15. //Pedimos el ingreso de datos al usuario.
16. write('Ingaresa el valor de la base: ');
17. readln(base);
18. write('Ingresa el valor de la altura: ');
19. readln(altura);
20.
21. //Llamamos a la función calcularArea.
22. area:= calcularArea(base,altura);
23. writeln('El área del triángulo es: ',area:1:2);
24. END.
El funcionamiento de este programa es básicamente el mismo al del anterior con la diferencia de que en vez
de un procedimiento usamos una función además de que declaramos una nueva variable que contendrá el área del
triángulo.
Veamos el encabezado de la función:

Function calcularArea(b, h: real): real;

Como vemos hemos declarado dos parámetros tal como lo habíamos hecho en el procedimiento. Ambos
parámetros contienen valores reales. El tipo de la función lo hemos declarado como real y por tanto esta devolverá un
número como resultado de sus tareas. El tipo de la función no tiene nada que ver con el tipo de sus parámetros, en
este caso da la casualidad de que todo es del tipo real, pero pueden ser cualesquiera siempre y cuando el de la función
sea un tipo simple.
Al igual que hacíamos en el procedimiento, en la función verificamos primero que las dimensiones ingresadas
sean mayores que 0, de lo contrario diremos que el área vale 0 mediante la asignación de la línea 11. Allí pueden ver
que utilizamos la función como si fuera una variable y le asignamos el valor 0. Vemos esto también en la línea 9
donde calculamos el área en caso de que las dimensiones sean válidas y le asignamos a la función el valor de
multiplicar el parámetro b por el parámetro h y dividir ese resultado entre 2.

Vladimir Rodríguez 84
Programación estructurada en Pascal

La función puede usarse a la izquierda de una expresión de asignación (:=) solo si estamos dentro de su
bloque principal y solo cuando queramos asignarle el valor que nos devolverá. Fuera de su cuerpo la función puede
ser usada solo a la derecha de las expresiones de asignación, ya sea para realizar cálculos o para que otras variables
tomen su valor, tal como sucede en la línea 22 de nuestro programa.
Mediante la expresión de asignación area:= calcularArea(base,altura) estamos asignándole a la
variable area el valor que devolverá calcularArea con los parámetros ingresados.

Otro modo de haber escrito este programa habría sido no declarar la variable area y escribir la instrucción
WRITELN de la línea 23 de esta manera:

writeln('El área del triángulo es: ', calcularArea(base,altura):1:2);

Como pueden observar, incluimos dentro de la instrucción WRITE directamente la llamada a la función
calcularArea para mostrar en pantalla su valor.

Funciones Booleanas:

El tipo Boolean es un tipo simple que toma uno de dos valores posibles: TRUE (verdadero) o FALSE (falso).
Esto por supuesto ustedes ya lo saben. Las funciones pueden ser del tipo boolean ya que como acabo de decir es un
tipo simple. El hecho de que me tome un trabajo extra para tratarlas es para que ustedes puedan ver las formas más
comunes en las que estas son utilizadas.
Ahora veremos el ejemplo del área del triángulo donde tendremos dos funciones, una para verificar que lo
ingresado por el usuario corresponde a dimensiones adecuadas para el triángulo, en tal caso la función devolverá
TRUE y en caso contrario FALSE. Si las dimensiones son correctas entonces llamaremos a la función para calcular el
área del triángulo para la cual solo nos limitaremos a hacer el cálculo ya que siempre la llamaremos en caso de que
los datos ingresados sean correctos.

1. PROGRAM triangulo;
2.
3. Var
4. base, altura, area: real;
5.
6. Function verificarDatos(b, h: real): boolean;
7. Begin
8. verificarDatos:= (b>0) and (h>0);
9. End;
10.
11. Function calcularArea(b, h: real): real;
12. Begin
13. calcularArea:= b*h/2;
14. End;
15.
16. BEGIN
17. //Pedimos el ingreso de datos al usuario.
18. write('Ingaresa el valor de la base: ');
19. readln(base);
20. write('Ingresa el valor de la altura: ');
21. readln(altura);
22.
23. If verificarDatos(base,altura) then
24. writeln('El área es: ',calcularArea(base,altura):1:2)
25. else
26. writeln('Has ingresado alguna dimensión menor o igual a 0.');
27. END.

Este ejemplo debería de resultarles bien sencillo de entender. Tenemos dos funciones declaradas. Solo
explicaré el funcionamiento de la función verificarDatos: Como pueden ver tiene dos parámetros reales y la
función es del tipo boolean. La sentencia de la línea 8 es igual a la que se usa para las variables del tipo boolean, o
sea, dicha sentencia es equivalente a escribir:

If (a>0) and (h>0) then


verificarDatos:= true
else
verificarDatos:= false;

Vladimir Rodríguez 85
Programación estructurada en Pascal

Ambas formas funcionan de igual manera, pero la más correcta es la que yo utilicé en el programa. Aún así
ustedes siempre tienen la libertad de utilizar las herramientas que prefieran.
Veamos ahora el IF de la línea 23:

If verificarDatos(base,altura) then

Como ya dije, las funciones pueden ser usadas como variables pero no pueden asignársele valores fuera de su
bloque principal. En este caso la estamos utilizando en el IF como una variable del tipo boolean y por ende estamos
preguntando si es TRUE, recuerden que solo incluir el nombre de un boolean es preguntar por su veracidad, y para
preguntar por su falsedad se utiliza el operador lógico NOT.

Ahora veamos un ejemplo un poco más complejo acerca del uso de una función booleanas. En este caso
veremos una función llamada Pertenece la cual se encargará de verificar si un elemento pertenece o no a un
arreglo. En caso afirmativo devolverá TRUE y en caso contrario FALSE:

1. PROGRAM BooleanFunction;
2.
3. Const
4. N= 5;
5.
6. Type
7. arreglo = array [0..N] of integer;
8.
9. Var
10. i, elem: integer;
11. a: arreglo;
12.
13. Function Pertenece(x: integer; A: arreglo): boolean;
14. Var
15. i: integer;
16.
17. Begin
18. i:= 1;
19.
20. While (i <= N) and (A[i] <> x) do
21. i:= i + 1;
22.
23. Pertenece:= i <= N;
24. End;
25.
26. BEGIN
27. Write(‘Ingresa ‘,N,’ enteros: ‘);
28. For i:=1 to N do
29. read(a[i]);
30. Writeln;
31. Write(‘Ingrese el elemento a buscar: ‘);
32. readln(elem);
33.
34. If pertenece(elem,a) then
35. writeln(‘El elemento ‘,elem,’ pertenece al arreglo.’)
36. else
37. writeln(‘El elemento ‘,elem,’ no pertenece al arreglo.’);
38.
39. END.

Quiero que ustedes mismos estudien este ejemplo hasta que lo comprendan. Verifíquenlo a mano si hace falta,
eso muchas veces funciona para comprender como funciona un programa y es muchas veces lo que hace falta para
encontrar un error que ni con el depurador (debuger) podemos hallar.

Vladimir Rodríguez 86
Programación estructurada en Pascal

Llamar a un subprograma desde otro subprograma:

Esto es realmente sencillo y según mi consideración no requiere ni de ejemplificación, solo una leve
explicación para que lo sepan nada más, aunque aún daré un ejemplito genérico.
Es posible llamar a un subprograma desde otro siempre y cuando el subprograma que estamos llamando fue
declarado antes que el subprograma desde el que estamos llamando. Veamos lo siguiente:

Procedure proc1(parámetros nominales);


Begin
//Instrucciones;
End;

Procedure proc2(parámetros nominales);


Begin
//Instrucciones
Proc1(parámetros efectivos);
End;

Como ven aquí tenemos dos procedimientos, Proc1 y Proc2, el primero declarado antes que el segundo. En
el cuerpo de Proc2 vemos una llamada a Proc1. Esto es posible porque Proc1 ya está declarado arriba, de lo
contrario el compilador nos diría que no encuentra el identificador especificado. Dado esto no podemos llamar a
Proc2 desde Proc1. Esto se aplica tanto para procedimientos como para funciones, y es posible llamar a una
función desde otra, a un procedimiento desde una función, a una función desde un procedimiento, etc., en fin, a un
subprograma desde otro.

Como habrán visto, hasta el momento hemos usado solo el pasaje de parámetros por valor tanto para
funciones como para procedimientos. La próxima sección de este manual explicará el pasaje de parámetros por
referencia. Esto llevará un poco de trabajo para su entendimiento, pero si se esmeran verán que no es tan complicado.

Espero que hasta ahora hayan entendido todo. Como ya saben pueden escribirme a mi casilla de correo
mstrvladi@hotmail.com para consultar cualquier duda que tengan y yo se las responderé a la brevedad. También
pueden sugerir ejercicios o señalar errores en el manual, cosas que tal vez resulten inentendibles, etc.

Vladimir Rodríguez 87
Programación estructurada en Pascal

Parámetros: Pasaje por Referencia:

Antes de comenzar con la explicación en sí de lo que significa el pasaje de parámetros por referencia he de
dar una leve y sumamente básica explicación acerca de lo que en verdad es una variable. Luego me extenderé un
poco hablando de la memoria RAM para que logren comprender esto lo mejor posible.
No se hasta que punto saben ustedes de computadoras, pero asumo que tienen un dominio bastante avanzado
si se han lanzado a la programación. Con esto me refiero a que saben usar una PC sin dificultad alguna, navegar en
Internet, descargar y subir archivos. También supongo que saben lo que es un disco duro y para qué sirve, qué es la
memoria RAM y para qué sirve, cuáles son los dispositivos de entrada y salida, etc. Aún así veamos un pantallazo
muy general acerca de la memoria RAM, que es lo que nos importa ahora.

Todos ustedes deben de haber visto que al obtener un nuevo programa este suele especificar los
requerimientos mínimos del equipo para que pueda funcionar. Lo más importante acerca de estos requerimientos es la
velocidad de procesamiento de la CPU, la capacidad de almacenamiento del disco duro y la cantidad de memoria
RAM; para los juegos también suele indicarse el mínimo requerido de capacidad y velocidad de la GPU (tarjeta
gráfica). Como deberían saber, la CPU (Central Processing Unit) o Unidad Central de Procesamiento corresponde al
cerebro de la computadora y es la que se encarga de procesar absolutamente toda la información que se esté
transfiriendo en el momento. De este modo la CPU envía luego diferentes instrucciones a los diferentes componentes
de la computadora para que estos realicen sus propias tareas.
Como han visto ahora que saben lo que es programar, una CPU se dedica a seguir instrucciones bien
estructuradas. Las sigue paso a paso tal y como son dictadas por los programadores. Por eso decimos que una
computadora no es inteligente, es estúpida ya que hace exactamente lo que se le dice y nada más. Claro que existen
algoritmos de inteligencia artificial que permiten “pensar” a una computadora, pero no tengo idea de cómo funcionan
y además no son relevantes aquí.
Las instrucciones que sigue la CPU son cargadas en la memoria RAM solo mientras se necesiten allí, luego
son eliminadas para liberar la memoria y dejar lugar a otras instrucciones. También se libera toda la RAM al apagar
el PC. Esta memoria está dividida en celdas que a su vez están dividas en celdas que representan bits de información.
En dichas celdas es donde se guardan las variables, las constantes, etc. Dependiendo de la información que contenga
una variable o algún otro elemento será la cantidad de celdas de memoria que se necesitarán para almacenarlo. No es
lo mismo guardar un entero que un arreglo de enteros, claramente el segundo es mucho más grande que el primero.
Como pueden ver en la imagen, tenemos un pequeño
trozo de código Pascal y una representación de la memoria RAM.
En el código Pascal podemos ver la declaración de una variable
entera a que es inicializada con el valor 10. Lo que en realidad
estamos haciendo es ocupar una celda de memoria, o parte de
ella, con el valor entero 10. Lo que para nosotros representa el
identificador a es el nombre de la variable, pero en realidad para
la CPU esa es la dirección de la celda de memoria donde se está
guardando la información de esa variable.
Cuando nosotros declaramos constantes o variables lo que
estamos haciendo en realidad es guardar celdas de memoria para
almacenar su información. Los identificadores (nombres) solo
contienen la referencia a la celda de memoria donde se guarda la
información. Es por esto que no podemos duplicar
identificadores, sería como decirle al CPU “Guarda la
información de esta variable en tal celda de memoria, y la de
esta otra variable también allí, pero ambas deben ser
independientes eh”. Por este motivo es bueno que un programa
utilice siempre la menor cantidad de variables posible, para que
necesite menos memoria RAM para poder ejecutarse. Fíjense que normalmente los programas son muy complejos y
tienen miles de variables, tipos, constantes y estructuras diferentes. Piensen nada más en el programa que están
usando ahora para leer este texto si es que lo hacen en su computadora, o piensen en el procesador de texto que yo
usé para escribirlo. ¿Cuántas variables hay? Además también se carga la interfaz gráfica para que nosotros veamos
botoncitos, menúes e íconos, cuadros de texto, etc. Hay miles de estructuras dentro de dicho programa que se dedican
a ir verificando si lo que se escribe tiene faltas de ortografía o no, estructuras de búsqueda, de corrección, de
numeración, etc. Podría escribir un único manual dedicado al análisis de un procesador de textos avanzado como lo
es Microsoft Word u OpenOffice Writer.

Cuando nosotros escribimos una expresión de asignación del tipo x:= y; estamos diciendo: “Ve a la celda
donde está la información de Y y copia esta información en la celda donde guardas la de X.”. Esto solo copia los
contenidos de las celdas de memoria, pero ambas siguen siendo independientes. Lo mismo pasa cuando llamamos a
un subprograma y le damos parámetros de entrada, lo que hacemos es copiar el contenido de las celdas de memoria
de los parámetros efectivos en los nominales, pero unos siguen siendo independientes de otros. Esto no sucederá en el
pasaje de parámetros por referencia ya que en realidad no pasaremos los contenidos de las celdas sino la referencia de
su ubicación en la memoria.

Vladimir Rodríguez 88
Programación estructurada en Pascal

Bien, veamos entonces lo que significa el pasaje de parámetros por referencia. Primero lo explicaré tal como
es y luego lo detallaré con ilustraciones y ejemplos para que puedan comprenderlo bien ya que esto es un tema muy
importante para la programación y lo necesitarán si pretenden lanzarse a aprender un lenguaje más avanzado que el
Pascal. Como ya dije, en el pasaje de parámetros por referencia no copiamos el contenido de una celda de memoria
en otra sino que copiamos la referencia de su ubicación en la memoria. Veamos qué cornos significa esto: Cuando
hablo de referencia a una celda de memoria me refiero a su dirección. Cada celda tiene una dirección única en la
memoria, o sea, lo que indica su ubicación. Imaginen que cada celda de memoria es un hogar diferente disponible
para habitar; al igual que en el mundo real, cada hogar tiene una dirección única. No importa cómo se escribe esa
dirección ni como la interpreta la CPU, solo importa que cada celda tiene una única dirección que la identifica.
Si les parece mejor, imaginen que esa dirección es una flecha que le dice a la variable “Esa celda de allí es
donde guardarás tu información”. Veamos varios dibujos ilustrando primero las asignaciones normales (lo que
hemos visto hasta ahora) y luego las de pasaje por referencia. Supongan que declaramos tres variables tal como lo
ven en la Figura 1. En ese momento se asignarán lugares de memoria para dichas variables pero como no les hemos
asignado ningún valor no sabemos qué información pueda contener cada celda. Luego, en al Figura 2, le asignamos a
cada variable un valor, entonces el programa mirará la flecha de esa variable y asignará ese valor a la celda que le
corresponde sustituyéndolo por el valor anterior:

Var
Figura 1 x, y: integer; Figura 2 X:= 10;
Y:= 15;
a: char; a:= ‘c’;

15
? x x

? y 10 y
a a
? c

Ahora imaginen que luego de las asignaciones de la Figura 2 realizamos la de la Figura 3:

Como pueden ver allí lo único que hemos hecho


Figura 3 y:= x;
fue asignarle a y el valor de x y por lo tanto lo
que sucedió fue que se copió el valor que estaba
en la celda de x hacia la celda de y.
También puede verse que cada celda sigue
siendo independiente y cada variable sigue
manteniendo su misma celda, o sea, cada flecha
sigue apuntando al mismo lugar que antes.
10 Eso es lo que no sucederá en el pasaje por
x referencia ya que lo que hacemos en ese caso es
cambiar la dirección de las flechas.
Es importante notar que las celdas no se
guardan en ningún orden específico, por eso la
celda de x está más abajo que la de y a pesar de
10 y que la primera variable se declara antes.

Veamos un ejemplo de un procedimiento


a llamado proc1 con tres parámetros, num1,
c num2 y letra, a los cuales les pasamos sus
valores con el pasaje por valor tal y como lo
hemos venido haciendo hasta ahora:

Vladimir Rodríguez 89
Programación estructurada en Pascal

Figura 4

Var
10 x x, y: integer;
a: char;

15 Y Procedure proc1(num1, num2: integer; letra:


char);
(. . .)
c a //Bloque del procedimiento.

num1 BEGIN
10 x:= 10;
y:= 15;
a:= ‘c’;
num2
15
proc1(x,y,a);
letra END.
c

Pues bien, veamos lo que ilustra esta figura. Declaramos las variables globales x, y como enteros y a
como carácter. El procedimiento tiene los tres parámetros que ya nombre los cuales también funcionan como
variables (eso ya lo expliqué antes). Al comenzar el programa le asignamos a cada variable global un valor específico
de acuerdo a su tipo y luego llamamos al procedimiento indicando qué valor de cada variable debe asignarse a cada
parámetro. Eso lo hacemos al colocar las variables entre paréntesis al llamar al procedimiento ya que el orden en que
esas se colocan es el que indica cual variable corresponde a cual parámetro. De ese modo copiamos el valor de x a
num1, el de y a num2 y el de a a letra. A los efectos de lo que sucede en memoria eso es lo mismo que hacer las
siguientes asignaciones:
num1:= x; num2:= y; letra:= a;

Ahora veamos el mismo ejemplo pero pasaremos por referencia el valor del parámetro letra, los otros
dos seguirán siendo por valor. Aquí podrán apreciar qué es lo que le indica al programa cuál parámetro se toma por
referencia y cual no. Usaré dos figuras para este ejemplo, una para antes de la llamada al procedimiento y otra para
luego de la misma:

Vladimir Rodríguez 90
Programación estructurada en Pascal

Figura 5

10 x Var
x, y: integer;
a: char;

15 Y
Procedure proc1(num1, num2: integer; var letra:
char);
c a //Bloque del procedimiento.

num1 BEGIN
? x:= 10;
y:= 15;
a:= ‘c’;
num2
? END.

letra
?

Vean que el encabezado del procedimiento sigue siendo igual excepto porque usamos la palabra reservada
VAR antes de declarar el parámetro letra. Usar esta palabra antes de declarar los parámetros indica que los valores
de estos serán tomados por referencia. Ahora veamos lo que sucede luego del llamado al procedimiento:

Figura 6

Var

10 x x, y: integer;
a: char;

15 Y Procedure proc1(num1, num2: integer; var letra:


char);
//Bloque del procedimiento.

c a
BEGIN
x:= 10;
num1
10 y:= 15;
a:= ‘c’;

num2
15 proc1(x,y,a);
END.
letra

Vladimir Rodríguez 91
Programación estructurada en Pascal

¿Qué sucedió? Pues bien, como ya dije antes, en el pasaje de parámetros por referencia estamos pasando la
dirección de una celda y no su valor, por tanto como el parámetro letra es pasado por referencia le estamos
diciendo que tome la misma celda de memoria que a para guardar sus valores. Esto implica que ahora hay dos
variables que utilizan la misma celda para almacenarse y por lo tanto si modificamos una modificamos la otra y que
apuntan a lo mismo, o sea, si dentro del bloque del procedimiento hacemos una asignación letra:= ‘z’; el
valor de la celda que ahora dice c cambiará a z y por lo tanto, a también valdrá z y no c. Lo mismo si hacemos al
revés, imaginen que asignamos a:= ‘h’; dentro del procedimiento. El parámetro letra también valdrá h ya que
mira a la misma celda. En simples palabras, a y letra pasan a formar uno mismo, o sea, es como si nombráramos
la misma cosa de dos formas diferentes. Da lo mismo si la llamamos a o letra, modifican y responden lo mismo en
tanto miren a la misma celda de memoria.
Este efecto durará solo dentro del bloque principal del procedimiento. Una vez salgamos de allí todo
volverá a la normalidad.

Tal vez piensen que podría haber explicado esto de un modo más corto, pero créanme que no basta solo con
decir “En el pasaje de parámetros por referencia el parámetro efectivo y el nominal pasan a ser uno mismo durante
toda la ejecución del subprograma.” Esto generará muchas dudas a la hora de trabajar, incluso ahora las tendrán
cuando comiencen con los ejercicios.

Importante: Declarado un parámetro por referencia solo puede usarse una variable como parámetro
efectivo y no un valor específico. Esto significa que para este procedimiento Procedure proc1(num1, num2:
integer; var letra: char); no podemos hacer una llamada como esta: proc1(x,y,10); pero sí una
proc1(x,5,a); ya que num1 y num2 son parámetros que se toman por valor.

Veamos ahora dos ilustraciones más. En la Figura 7 vemos lo que sucede si los parámetros num1 y num2
se toman por referencia y letra por valor, y en la Figura 8 vemos lo que sucede si todos son parámetros tomados
por referencia:

Figura 7
Var
10 x x, y: integer;
a: char;

15 Y Procedure proc1(var num1, num2: integer; letra:


char);
//Bloque del procedimiento.
c a
BEGIN

num1 x:= 10;


y:= 15;
a:= ‘c’;

num2
c proc1(x,y,a);
END.

letra

Vladimir Rodríguez 92
Programación estructurada en Pascal

Figura 8
Var
10 x x, y: integer;
a: char;

15 Y Procedure proc1(var num1, num2: integer; letra:


char);
//Bloque del procedimiento.
c a
BEGIN

num1 x:= 10;


y:= 15;
a:= ‘c’;

num2 proc1(x,y,a);
END.

letra

Dado que utilizar pasaje de parámetros por referencia modifica al parámetro efectivo usamos estos como
parámetros de entrada/salida a un procedimiento. Esto es muy útil ya que a diferencia de una función podemos
obtener muchos datos de salida en vez de uno solo.

NOTA: No es recomendable utilizar el pasaje de parámetros por referencia para las funciones.

Con esto quedaría abarcado todo lo referente a los subprogramas y por tanto es hora de practicar todo lo
que han leído ya que sin práctica no avanzarán un pelo. Es totalmente imprescindible que los hagan todos o de lo
contrario no servirá de nada todo lo dado hasta ahora. Los más importantes son los últimos, escritos en letra roja los
que corresponden a un parcial de facultad. Todo lo dado en las clases es lo que yo les he explicado aquí y los
ejercicios que les he puesto, incluso los enlaces de inernet, perteneces a la facultad y por tanto ustedes son totalmente
capaces de resolver estos ejercicios habiendo leído este manual y habiéndose esforzado en realizar los ejercicios
planteados y estudiar los ejemplos que les he puesto.

Ejercicios:

Ejercicio 1: Expliquen la diferencia entre un parámetro formal (o nominal) y un parámetro verdadero (o


efectivo).

Ejercicio 2: Escriban un procedimiento con solo tres parámetros enteros: a, b y c. El efecto del
procedimiento debe ser “girar” los valores de los parámetros hacia la derecha de manera que, después de la ejecución,
el valor que originalmente estaba en a quede en b, el que estaba en b quede en c y el que estaba en c quede en a.

Ejemplos de entrada:

4 1 7
-12 6 2

Ejemplos de salida:

7 4 1
2 -12 6

Vladimir Rodríguez 93
Programación estructurada en Pascal

Ejercico 3: Consideren el siguiente programa :

1 PROGRAM alcance;
2 Var tum, num, temp: Integer;
3 Procedure Prog (a, b: integer; VAR c: integer);
4 Var reloj: Integer;
5 Begin
6 reloj:= a * b;
7 reloj:= reloj + 1;
8 c:= reloj + a;
9 WriteLn(a, b, c, reloj)
10 End;
11 (*Programa principal*)
12 BEGIN
13 tum:= 1;
14 num:= 2;
15 Prog(tum, num, temp);
16 WriteLn(temp);
17 tum:= 0;
18 num:= 1;
19 Prog(tum, num, temp);
20 WriteLn(temp)
21 END.

1. Identifiquen la/s variable/s globales del programa principal.


2. Identifiquen la/s variable/s local/es declarada/s en el procedimiento Prog.
3. Identifique los parámetros formales del procedimiento Prog. Determine los parámetros de entrada (de
valor) y de salida (por variable).
4. Determine los parámetros por valor verdaderos (efectivos) en la primera invocación del procedimiento.
5. Determine los parámetros por valor verdaderos (efectivos) en la segunda invocación del procedimiento.
6. Determine la salida estándar del programa. ¿Cuántas líneas se escribirán?

Ejercicio 4: Dado el siguiente procedimiento

PROCEDURE Prueba (x: Real; y: Integer; VAR z: Real);

y las siguientes variables

VAR tiempo : Real;


tiempo_i : Integer;

Determinen cuáles de las siguientes invocaciones son correctas. Justifiquen.

Prueba( 1, 2.0, tiempo );


Prueba( tiempo_i, 3, tiempo );
Prueba( tiempo_i, 3, 2.0 );
Prueba( 1, 3, tiempo_i );
Prueba( 5 * tiempo_i, round( 7.3 ), tiempo ); (* tiempo_i ya fue inicializada *)
Prueba( tiempo, 3, tiempo ); Prueba( Prueba( 5, 33.8, tiempo ), 92, tiempo );

¿Cuáles son los errores en las invocaciones incorrectas?

Ejercicio 5: Implementen un procedimiento que dado un arreglo de enteros devuelva el valor más grande y
el índice en que éste ocurre. El largo del arreglo es dado por una constante N global al procedimiento.

Ejercicio 6: Escriban una función llamada conv que convierta un arreglo de caracteres, cada uno de los
cuales contiene un dígito decimal, al entero equivalente.

Vladimir Rodríguez 94
Programación estructurada en Pascal

Por ejemplo, si el parámetro de entrada es [ `1′, `2′, `3′ ], entonces el entero resultante será 123. El largo del arreglo
es dado por una constante N global a la función.

Ejercicio 7: Implementen una función que dados 2 arreglos de caracteres, determine si el primero es prefijo
del segundo. Luego reescriban el Ejercicio 2 de la página 63 utilizando esta función. En caso de que la cadena más
corta pertenezca a la más larga, despliegue en la salida estándar la posición en que comienza la coincidencia.
Tomando el primer ejemplo dado en la letra de dicho ejercicio, la salida sería:

El texto 'tor' se encuentra dentro del texto 'totora' ( posición de


comienzo: 3 ).

En caso de que no haya coincidencia, el mensaje deberá ser el mismo que en el ejercicio 2 de la página 63.

Ejercicio 8: Dado el siguiente programa:

PROGRAM principal (input, output);


VAR a, b : Integer;

PROCEDURE a1 (VAR x : Real);


VAR c : char;

PROCEDURE a2 (y : char);
VAR d : Integer;
BEGIN
. . .
END;

BEGIN
. . .
END;

PROCEDURE b1;
VAR e : Integer;
BEGIN
. . .
END;

BEGIN
. . .
END;

Indiquen las variables a las que se puede hacer referencia dentro del alcance del procedimiento a2, las que
se pueden referenciar dentro de a1, las que se pueden referenciar dentro de b1 y las variables globales.

Ejercicio 9: Determinen la salida del siguiente programa si se obtiene de la entrada estándar el entero 4
(elegir la opción a, b, c o d).

PROGRAM Ppal (input, output);


VAR x : Integer;

PROCEDURE a (VAR y : Integer);


BEGIN
y := y * 2;
END;

PROCEDURE b (x : Integer);
BEGIN
x := x + 5;

Vladimir Rodríguez 95
Programación estructurada en Pascal

END;

BEGIN
ReadLn (x);
b (x);
WriteLn (x);
a (x);
WriteLn (x);
END.

a 4 b4 c9 d9
8 9 18 8

Ejercicio 10: Determinen la salida del siguiente programa:

PROGRAM Detectar (input, output);


VAR a, b, c, d : Integer;

PROCEDURE pal (y: Integer; VAR z: Integer);

VAR c : Integer;

BEGIN
b := y * z;
c := b + z;
y := y + 1;
z := z * (z + 1);
WriteLn (b:3, c:3, y:3, z:3)
END;

BEGIN
a := 2;
c := 3;
d := 5;
pal (c, d);
WriteLn (a:3, b:3, c:3, d:3);
b := 4;
pal (b, a);
WriteLn (a:3, b:3, c:3, d:3)
END.

El simbolo - representa el salto de línea.

A: 15 20 4 30- 2 15 4 30- 9 10 9 6- 6 9 4 30
B: 15 20 4 30- 2 15 4 5- 9 10 9 6- 2 9 4 5
C: 15 20 4 30- 2 15 3 30- 8 10 5 6- 6 8 3 30
D: 15 20 4 30- 2 15 3 5- 8 10 5 3- 2 8 3 5

Ejercicio 11: Indiquen cual sería la salida del siguiente programa suponiendo que como entrada se le
proporciona el último dígito de su número de cédula de identidad. Por ejemplo, si su número de cédula es el
1.234.567-8, entonces el número que se ingresa es el 7.

Program Ej1 (input,output);


var z: integer;

function f (y : integer) : boolean;


begin
z := z + y;
f := y mod 2 = 0
end;

procedure a (var y : integer);

Vladimir Rodríguez 96
Programación estructurada en Pascal

begin
y := 2 * y;
end;

procedure b (var z : integer);


begin
z := z + 5;
end;

begin
readln(z);
if f(z) then a(z) else b(z);
writeln('Salida = ', z)
end.

Ejercicio 12: Indique cual sería la salida del siguiente programa suponiendo que como entrada se le
proporciona el último dígito de su número de cédula de identidad. Por ejemplo, si su número de cédula es el
1.234.567-8, entonces el número que se ingresa es el 7. El orden de evaluacion de las expresiones numericas depende
de cada compilador. En este caso, asuma que ese orden es de izquierda a derecha.

program jul2002 (input,output);


var z: integer;

function g (var x: integer):integer;


begin
x := x + 1;
z := z + 1;
writeln('Salida en g1:',x + z);
g := x + z
end;

function f (x: integer; var y: integer): integer;

function g(x: integer): integer;


begin
x := x + 1;
writeln('Salida en g2:',x + z);
g := x + z
end;

begin
x := x + 1;
z := z + x - y + 2;
if x mod 2 = 0 then y := y + 1
else y := y - 1;
x := g(x) + y;
writeln('Salida en f:',x);
f := x
end;

procedure p (var x,y: integer);


begin
y := f(x,y) + z;
y := y + g(x) + x;
x := x + 1;
writeln('Salida en p:',y)
end;

begin
readln(z);
p(z,z);
writeln('Salida final:',z + 1);
end.

Vladimir Rodríguez 97
Programación estructurada en Pascal

Vladimir Rodríguez 98
Programación estructurada en Pascal

QUINTA PARTE

Enumerados

Registros con y sin variante

Arreglo con tope

Vladimir Rodríguez 99
Programación estructurada en Pascal

Introducción:

Volvemos una vez más a los tipos definidos por el programador. Hasta ahora trabajamos con los Subrangos,
que podían tomar valores consecutivos dentro de un rango específico contenido en un rango mayor; y los Arreglos,
que correspondían con tablas de datos de un mismo tipo. Dichas tablas podían tener una dimensión, dos, tres o
muchas más.
Luego de eso cambiamos de tema para pasar a los Procedimientos y las Funciones, que correspondían con
pequeños programas dentro del programa principal cuya función era realizar una tarea específica. Ahora hace falta
terminar de ver los tipos que podemos definir nosotros mismos como programadores, y nos quedan tres: Enumerados,
Conjuntos (casi no trabajaremos con ellos) y Registros.

Lo verdaderamente importante aquí son los Registros ya que estos son capaces de almacenar varios datos
diferentes y de diferentes tipos cada uno, pero esto ya lo ampliaremos en su momento.

Vladimir Rodríguez 100


Programación estructurada en Pascal

Enumerados:

Los Enumerados, tal como su nombre lo indica, son enumeraciones de ciertos elementos específicos y que
generalmente corresponden a un mismo grupo de calificación. Por ejemplo, los días de la semana corresponden todos
a un mismo grupo (días de la semana); los meses del año también son un grupo específico, y así con muchas otras
cosas que se nos pueden ocurrir. Básicamente un enumerado es un conjunto de elementos que nosotros definiremos
uno a uno.
Realmente no resultará difícil trabajar con este tipo de datos. Veamos primero su declaración:

Type
diasSemana=(domingo, lunes, martes, miercoles, jueves, viernes,
sabado);
Mes= (enero, febrero, marzo, abril, mayo, junio, julio, agosto,
setiembre, octubre, noviembre, diciembre);
puntoCardinal= (norte, sur, oeste, este);

Utilizamos primero la palabra Type y luego, al igual que antes, le damos un nombre al tipo seguido de un
signo de igual. Luego colocamos entre paréntesis y separados por comas cada elemento del enumerado. De este modo
tenemos allí uno que contiene todos los días de la semana, otro con todos los meses del año y otro con los cuatro
puntos cardinales.

La declaración genérica de un tipo enumerado sería así:

Type nombreDelTipo= (elemento1, … ,elementoN);

donde cada elemento dentro de los paréntesis corresponde a un identificador cualquiera. Así, los elementos de los
enumerados, pueden llamarse de cualquier manera pero jamás repetirse, jamás pueden comenzar con números o
caracteres especiales ni contener espacios; como ya dije, son identificadores y por ende han de cumplir las mismas
reglas que estos.

Visto de esta manera no resulta muy sencillo hallarle una utilidad a los enumerados, sin embargo podremos
ver que sí la tienen cuando usemos registros. Por este motivo ahora veremos solo aspectos generales de este tipo de
datos para luego poder aplicarlos realemente con lo registros.

Para empezar, cada elemento que asignamos a un enumerado tiene un ordinal de acuerdo a la posición en la
que está nombrado comenzando a contar desde el número 0. De este modo para el tipo diasSemana tendríamos
los siguientes ordinales:

ord(domingo)= 0
ord(lunes)= 1
ord(viernes)= 5
ord(sabado)= 6

De modo inverso podemos obtener un elemento de un enumerado dado su ordinal. Para el tipo diasSemana
esto sería así:

diasSemana(0)= domingo
diasSemana(3)= miercoles

Aplicando estas dos propiedades a los tipos Mes y puntoCardinal podríamos tener lo siguiente:

Mes(0)= enero puntoCardinal(0)= norte


Mes(11)= diciembre puntoCardinal(3)= este
ord(octubre)= 9 ord(sur)= 1

Recuerden siempre que la función ORD devuelve un valor del tipo integer.
Ahora díganme cuál sería la salida que obtendríamos de las siguientes sentencias si pertenecieran a un
programa donde están declarados los enumerados anteriores:

writeln(ord(Mes(0)));
writeln(puntoCardinal(ord(domingo)));

Es sumamente importante que al intentar obtener un elemento de un enumerado dado por su ordinal jamás
utilicemos un entero que esté por fuera del rango de elementos del enumerado. Esto significa que, por ejemplo, para
el tipo diasSemana no escribamos algo como esto: diasSemana(7), diasSemana(-1),

Vladimir Rodríguez 101


Programación estructurada en Pascal

diasSemana(1.5). Como ven, en el primer caso estamos yéndonos un lugar fuera del enumerado, en el segundo
caso estamos pidiendo el elemento de una ubicación negativa la cual jamás existirá y en el tercer caso pasamos como
parámetro un real cuando solo se admiten enteros, dato este último que no les había dicho.

Read y Write con enumerados:

Como primera cosa les digo que... NO ES POSIBLE HACER READ Y/O WRITE con los enumerados. Esto
significa que, declarada una variable de un tipo enumerado, no podemos incluir esta dentro de un procedimiento
READ, READLN, WRITE o WRITELN. Por ejemplo, para los enumerados anteriores, el siguiente fragmento de
código estaría mal:

Type
diasSemana=(domingo, lunes, martes, miercoles, jueves, viernes,
sabado);
mesAño= (enero, febrero, marzo, abril, mayo, junio, julio,
agosto, setiembre, octubre, noviembre, diciembre);
puntoCardinal= (norte, sur, oeste, este);

Var
dia: diasSemana;
mes: mesAño;
punto: puntoCardinal;

BEGIN
read(dia);
mes:= enero;
writeln(' ',enero);
punto:= sur;
writeln('El día ',dia,' de ',mes,' fuimos al ',punto,'.');
END.

Si han entendido lo que he hablado de enumerados hasta ahora podrán darse cuenta rápidamente de que los
errores en este código están dados por el uso de variables de tipos enumerados en procedimientos READ y WRITE.
Las asignaciones sí son correctas.
Noten que para inicializar una variable de tipo enumerado con un valor específico lo hacemos como con
cualquier otra, o sea, damos el nombre de la variable seguido del símbolo de asignación := y luego el valor que le
queremos dar escrito tal cual lo nombramos nosotros al incluirlo en el enumerado.

Comparaciones con enumerados:

Según el orden de enumeración en que están dados los elementos de un enumerado tenemos los mismos
símbolos de comparación que para los números:

< Menor que


<= Menor o igual que
= Igual que
>= Mayor o igual que
> Mayor que
<> Distinto que

Por ejemplo, para el tipo puntoCardinal= (norte, sur, oeste, este) tenemos que
norte<sur ya que el primero está en la posición 0 y el segundo en la posición 1. Lo mismo si la comparación fuera
de diferencia: norte<>sur.
Supongo que no debería decir que no es posible comparar variables de tipos enumerados diferentes ya que
serían variables de distintos tipos, o sea, para nuestro fragmento de código anterior no sería posible comparar la
variable dia con la variable mes ya que son de tipos diferentes, una del tipo diasSemana y la otra del tipo
mesAño.

Como debería resultar obvio ya, los tipos enumerados son tipos ordinales, o sea, tienen una cantidad finita de
elementos y dado cualquiera de ellos es posible saber cuál es el anterior y cual es el siguiente al mismo. Por lo tanto
para estos tipos de datos podemos utilizar las funciones predefinidas de Pascal succ y pred, la primera para obtener
el sucesor de un elemento siempre y cuando no nos estemos refiriendo al último, y la segunda para obtener el

Vladimir Rodríguez 102


Programación estructurada en Pascal

predecesor de un elemento siempre y cuando no nos estemos refiriendo al primero. Volviendo a ejemplificar con los
tres tipos enumerados definidos al inicio de esta sección tendríamos lo siguiente:

succ(lunes)= martes
pred(noviembre)= octubre
succ(este) //No tiene sucesor, es el último elemento.
pred(domingo) //No tiene predecesor, es el primero.

En la mayoría de los ejemplos que he dado aquí acerca de estas dos funciones, de las comparaciones y de la
utilización de ordinales para enumerados he utilizado directamente los elementos del enumerado, o sea, si miran aquí
arriba hay cosas como succ(lunes)= martes, cuando en general lo que se usa son variables del tipo
enumerado. Con esto quiero decir que si, por ejemplo tuviéramos una variable declarada como la siguiente

Var
dia: diasSemana;

BEGIN
dia:= lunes;
END.

podríamos utilizar a dicha variable dentro de las funciones de sucesor y predecesor así como también en
comparaciones y la función ORD. Por ejemplo:

succ(dia)= martes;
pred(dia) //No tiene sucesor.
ord(dia)= 1 //Es el segundo elemento del enumerado.
dia<>martes //Es verdadero ya que la variable dia es igual a lunes.

Bien, ahora veamos un pequeño ejemplo de programa en el cual tendremos un enumerado con los días de la
semana, otro con los meses del año y un subrango de enteros que irá desde el número 1 al 31. El usuario elegirá un
día de la semana, la fecha correspondiente al mismo día y el mes, tras lo cual el programa desplegará la fecha en
formato DÍA/MES/AÑO y en formato de oración. Veámoslo:

1.PROGRAM enumerados;
2.
3.Type
4. diasSemana= (domingo, lunes, martes, miercoles, jueves, viernes, sabado);
5. diasMes= 1..31;
6. mesAnio= (enero, febrero, marzo, abril, mayo, junio, julio, agosto,
7. setiembre, octubre, noviembre, diciembre);
8.
9.Var
10. dia: diasSemana; //Guarda un día de la semana.
11. fecha: diasMes; //Guarda un día del mes.
12. mes: mesAnio; //Guarda un mes del año
13. anio, aux: integer; //anio guarda el año, aux es para leer enteros.
14.
15.BEGIN
16.{Pedimos al usuario que ingrese los datos----------------------------------}
17. //Pedimos el ingreso del año en cuestión.
18. write('Ingresa el año: ');
19. readln(anio);
20.
21. //Pedimos el ingreso del número del mes del año.
22. write('Ingresa un número correspondiente a un mes del año (1-12): ');
23. readln(aux);
24. mes:= mesAnio(aux - 1);
25.
26. //Pedimos el ingreso del número del día de la semana.
27. write('Ingresa un número correspondiente a un día de la semana (1-7): ');
28. readln(aux);
29. dia:= diasSemana(aux - 1);
30.
31. //Pedimos el ingreso del número de un día del mes.
32. write('Ingresa un número correspondiente a una fecha del mes (1-31): ');
33. readln(fecha);
34.
35. writeln;
36.
37.{Escribimos la fecha en formato DIA/MES/AÑO.-------------------------------}

Vladimir Rodríguez 103


Programación estructurada en Pascal

38. //Escribimos la fecha en formato DD/MM/AAAA.


39. writeln('La fecha ingresada DIA/MES/AÑO es: ',fecha,'/',ord(mes+1,'/',
anio,'.');
40.
41.{Escribimos la fecha en formato de oración---------------------------------}
42. //Primero escribimos el día.
43. Case dia of
44. domingo: write('Domingo ');
45. lunes: write('Lunes ');
46. martes: write('Martes ');
47. miercoles: write('Miércoles ');
48. jueves: write('Jueves ');
49. viernes: write('Viernes ');
50. sabado: write('Sábado ');
51. End;
52.
53. //Ahora escribimos la fecha del mes.
54. write(fecha,' de ');
55.
56. //Ahora escribimos el mes.
57. Case mes of
58. enero: write('Enero del año ');
59. febrero: write('Febrero del año ');
60. marzo: write('Marzo del año ');
61. abril: write('Abril del año ');
62. mayo: write('Mayo del año ');
63. junio: write('Junio del año ');
64. agosto: write('Agosto del año ');
65. setiembre: write('Setiembre del año ');
66. octubre: write('Octubre del año ');
67. noviembre: write('Noviembre del año ');
68. diciembre: write('Diciembre del año ');
69. End;
70.
71. //Escrbimos el año.
72. write(anio,'.');
73.END.

Analicemos un poco este código desde el comienzo de su bloque principal. Primero pedimos al usuario que
ingrese un año y lo leemos como entero. Luego en la línea 22 le indicamos que ingrese un número entre 1 y 12 para
indicar un mes del año, y en la línea siguiente le asignamos a la variable mes el elemento cuyo ordinal es igual al
valor indicado por el usuario menos 1. Esta resta se debe a que los meses del enumerado están numerados del 0 al 11
y el usuario ingresa números del 1 al 12. Aplicamos el mismo procedimiento para inicializar la variable dia. En
ambos casos usamos una variable auxiliar llamada aux para leer los enteros ingresados por el usuario.
Como último dato pedimos que se ingrese la fecha del día, o sea, a qué día del mes nos estamos refiriendo y
leemos este como un entero guardándolo en la variable fecha.
En la línea 39 escribimos la fecha en formato DD/MM/AAAA. Noten que en el procedimiento WRITELN de
dicha línea, para escribir el número del mes al que nos referimos escribimos el ordinal de la variable mes volviéndole
a sumar 1. Esto ya deberían entenderlo.
Ahora comienza lo interesante ya que utilizamos dos veces la función CASE. En el CASE de la línea 43
estamos diciendo: “Si la variable día es igual al valor domingo entonces escribe 'Domingo ', sino, si es igual al
valor lunes entonces escribe 'Lunes ',...”. Esta función es equivalente a una anidación de IF como la que sigue:

If dia=domingo then
write('Domingo ')
else
if dia=lunes then
write('Lunes ')
else
if dia=martes then
write('Martes ')...
Si entendieron eso deberían ser capaces de entender el CASE de la línea 57 ya que funciona exactamente
igual.

Como siempre les digo que ante cualquier duda o inquietud que tengan así como ante cualquier sugerencia
escríbanme a mstrvladi@hotmail.com y les responderé a la brevedad.

Ahora entreténganse con algunos ejercicios acerca de los enumerados.

Vladimir Rodríguez 104


Programación estructurada en Pascal

Ejercicios

Ejercicio 1: Consideren la siguiente declaración:

TYPE color = (rojo, blanco, azul, purpura);


VAR
coloracion : color;

Determinen cuáles de los siguientes fragmentos de código son válidos justificando la respuesta:

a) Read (rojo); d) coloracion := blanco;


WriteLn (rojo); CASE coloracion OF
rojo : WriteLn ('rojo');
blanco : WriteLn ('blanco');
azul : WriteLn ('azul');
purpura: WriteLn ('purpura')
END;

b) Read (coloracion); e) coloracion := azul;


Write (coloracion); CASE coloracion OF
rojo : WriteLn (rojo);
blanco : WriteLn (blanco);
azul : WriteLn (azul);
purpura: WriteLn (purpura)
END;

c) coloracion := blanco; f) IF coloracion = azul THEN


WriteLn (coloracion); WriteLn ('azul')
ELSE
WriteLn ('no azul')

Ejercicio 2: Determinen cuales de las siguientes declaraciones son válidas:


a) TYPE letra = ('X', 'Y', 'Z');
b) TYPE lenguaje = (Pascal, Fortran, Basic);
c) TYPE materias = (matematicas, historia, computacion, biologia);
carrera = (matematicas, computacion);

d)TYPE estado = (residente, ciudadano, extranjero);

nacionalidad = (americano, europeo, africano, asiatico, otra);

e) TYPE codigo = (1, 2, 3, 4, 5);


f) TYPE codigo = (c1, c2, c3, c4, c5);
g)TYPE estado = (soltera, casada, comprometida, divorciada);
VAR type : estado;

h) TYPE ciudad = (Paysandu, SanJose, Tacuarembo, Canelones);


VAR ciudad : type;

i) VAR ciudad : (Rivera, Salto, Soriano, Rocha);


j) PROCEDURE encontrar (VAR ciudad : (Minas, Florida, Flores));
k) TYPE trabajo = (obrero, oficinista, indefinido);
. . .
PROCEDURE buscar (VAR empleo : trabajo);

Vladimir Rodríguez 105


Programación estructurada en Pascal

Ejercicio 3: Es posible definir subrangos de enumerados ya que estos últimos corresponden a un tipo ordinal.
Examinen estas declaraciones:

TYPE tipodia = (Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);


VAR dia : tipodia;
laborable : Lunes..Viernes;
finsemana : Sabado..Domingo;

Indiquen cuál de las siguientes afirmaciones es correcta:

Se producirá un error de ejecución si se asigna el valor Martes a findesemana


Se producirá un error de ejecución si se asigna el valor Viernes a laborable.

Ejercicio 4: Examinen la siguiente definición :

TYPE raro = (gugol, nudol, brudol, cudol, zudol, budol);

Determinen el valor de las siguientes expresiones:

1. ord (gugol)
2. ord (zudol)
3. succ (brudol)
4. succ (budol)
5. pred (gugol)
6. ord (succ (zudol))

Ejercicio 5:

Obtengan el valor de las siguientes expresiones:

1. ord ('7') - ord ('0')


2. ord ('1') - ord ('1')
3. chr (3 + ord ('0'))
4. chr (0 + ord ('0'))

Ejercicio 6:: Examinen la siguiente declaración:

TYPE vocal = (a, e, i, o, u);


VAR letra : vocal;
uncar : char;

Determinen si se ejecutará sin error el siguiente código en PASCAL:


letra := a;
WHILE letra <= u DO
BEGIN
Read (uncar);
WriteLn (`El caracter capturado es ', uncar);
letra := succ (letra)
END
letra := u;
REPEAT
Read (uncar);
WriteLn (`El caracter capturado es ', uncar);
letra := pred (letra)
UNTIL letra = a

Vladimir Rodríguez 106


Programación estructurada en Pascal

Registros:

Los tipos de datos que hemos visto hasta ahora han podido ser primitivos (char, integer, real, boolean) o
estructurados (subrangos, arreglos, enumerados). Los tres tipos estructurados que vimos son homogéneos, o sea, que
todos los datos que contienen son de un mismo tipo. Por ejemplo, en un arreglo todas las celdas contienen datos de un
mismo tipo, ya sean enteros, reales, u otro tipo definidos por ustedes mismos. Un subrango no es más que un
intervalo más pequeño dentro de un conjunto de elementos mayor, pero sigue contiendo todos sus elementos de un
mismo tipo. Aquí es donde los registros se diferencian de todos los demás tipos ya que pueden contener muchos datos
y todos de diferentes tipos.
Por ejemplo, podríamos tener un registro que contenga caracteres, enteros, reales, cadenas, arreglos y mucho
más. Para ser un poco más específicos, podríamos crear un registro que dada una persona contenga su nombre,
apellido, edad, documento de identidad, dirección entre muchas otras cosas.

Declaración:

Bien, primero les diré como se declara genéricamente un registro, luego veremos un ejemplo de programa
donde guardaremos datos de dos personas, primero sin usar registros y luego usándolos.

Type
NombreDelRegistro= record
campo1: tipo;
campo2: tipo;
.
.
.
campoN: tipo;
End;

Como estamos hablando de un tipo resulta obvio que declararemos un registro debajo de la palabra TYPE. Al
igual que a los demás tipos definidos por ustedes mismos debemos darle un nombre seguido del signo de igual y
luego utilizamos la palabra reservada RECORD (registro en inglés). Después definimos cada uno de los campos que
contendrá tal como si fueran variables (en efecto eso son), o sea, le damos un nombre seguido por dos puntos y luego
le asignamos un tipo cualquiera recordando que si usaremos uno definido por nosotros mismos debemos haber
declarado este antes.
Veamos un ejemplo de declaración de un registro llamado ESTUDIANTE en el cual guardaremos el nombre
de un estudiante, su edad, su documento de identidad y el grado que cursa:

Type
Grados= (primero, segundo, tercero, cuarto, quinto, sexto);

Estudiante= record
nombre: string;
edad, documento: integer;
grado: Grados;
End;

Como ven primero tenemos un tipo llamado GRADOS que no es más que un enumerado de los grados
posibles que un estudiante puede cursar. Nuestro registro contiene cuatro campos (variables), una del tipo string, dos
del tipo integer y una última del tipo grados, el cual fue declarado previamente por nosotros mismos.
Ahora veamos los dos ejemplos de los que hablé para ver cómo funcionan los registros. En el siguiente
programa guardaremos el nombre, la edad, el documento de identidad y el grado de dos estudiantes distintos. Todos
estos datos los asignaremos nosotros manualmente internamente en el programa, o sea, no haremos READ de nada,
simplemente le daremos valores a nuestras variables y ya, como si nuestros estudiantes vinieran precargados en el
programa:

1. PROGRAM Reg1;
2.
3. TYPE
4. Grados= (primero, segundo, tercero, cuarto, quinto, sexto);
5.
6. VAR
7. NombreEstudiante1, NombreEstudiante2: String;
8. EdadEstudiante1, EdadEstudiante2: Integer;
9. DocumentoEstudiante1, DocumentoEstudiante2: Integer;

Vladimir Rodríguez 107


Programación estructurada en Pascal

10. GradoEstudiante1, GradoEstudiante2: Grados;


11.
12. BEGIN
13. NombreEstudiante1:= 'Fulano de Tal';
14. EdadEstudiante1:= 16;
15. DocumentoEstudiante1:= 12345;
16. GradoEstudiante1:= cuarto;
17.
18. NombreEstudiante2:= 'Mengano';
19. EdadEstudiante2:= 18;
20. DocumentoEstudiante2:= 16543;
21. GradoEstudiante2:= sexto;
22. END.

Muy bien, este programa no representa ningún tipo de dificultad para ustedes, simplemente declaramos
variables y las inicializamos con valores adecuados a su tipo. Tal vez debo aclarar por qué en las líneas 8 y 9 declaré
cuatro variables integer cuando podría haber usado una sola línea. Simplemente para separar las variables que se
corresponden con la edad a las que se corresponden con los documentos de identidad. Este programa sería mejor si
obtuviera los datos de la entrada estándar, sin embargo eso solo es utilizar READ en cada línea y ya. Ustedes mismos
pueden hacerlo si quieren.
Ahora veamos el mismo programa pero usando un registro:

1. PROGRAM Reg1;
2.
3. TYPE
4. Grados= (primero, segundo, tercero, cuarto, quinto, sexto);
5.
6. Estudiante= RECORD
7. Nombre: String;
8. Edad, Documento: Integer;
9. Grado: Grados;
10. END;
11.
12. VAR
13. Est1, Est2: Estudiante;
14.
15. BEGIN
16. Est1.Nombre:= 'Fulano de Tal';
17. Est1.Edad:= 16;
18. Est1.Documento:= 12345;
19. Est1.Grado:= cuarto;
20.
21. Est2.Nombre:= 'Mengano';
22. Est2.Edad:= 18;
23. Est2.Documento:= 16543;
24. Est2.Grado:= sexto;
25. END.

Bien. Como ven, luego del tipo Grados he declarado el tipo Estudiante que es un registro. Es el mismo que
usé como primer ejemplo. Dicho registro tiene cuatro campos que funcionan tal cual las variables que ya conocen y
se declaran de la misma manera. Como el registro es un tipo creado por nosotros mismos, debemos declarar variables
de ese tipo para poder usarlo. Eso hice en la línea 13 de nuestro programa, declaré las variables Est1 y Est2 del tipo
Estudiante. Cada una de estas variables contiene a las otras cuatro “dentro”. De este modo tendremos una variable
Nombre, una Edad, una Documento, y una Grados para Est1 así como otras cuatro para Est2.
Si queremos acceder a la variable Nombre de Est1 lo hacemos mediante la notación Est1.Nombre. Así con
todas las demás. De forma genérica, si tengo un registro genérico como el siguiente

MiRegistro= RECORD
Campo1: Tipo1;
Campo2: Tipo2;
. . .
CampoN: TipoN;
END;
y luego declaro una variable MiVariable: MiRegistro; , o sea, una variable llamada MiVariable del tipo
MiRegistro, para acceder al Campo1 escribo MiVariable.Campo1, lo mismo si quiero acceder al Campo2. O sea,
para acceder al campo número I, si I es un número entre 1 y N, escribo MiVariable.CampoI.

Vladimir Rodríguez 108


Programación estructurada en Pascal

Los campos de un registro son variables comunes y corrientes y por tanto todo lo que aplicamos a estas se
aplica a dichos campos.

Una estructura compleja:

Teniendo los registros podemos crear estructuras muy complejas. Simplemente citaré un ejemplo para que les
quede ya que lo necesitarán para trabajar en lo que se viene.
Supongan las siguiente declaración

TYPE
Registro= Record
Nombre: String;
Documento: Integer;
Edad: Integer;
End;

ListaRegistro= Array[1..7] of Registro;

VAR
MiLista: ListaRegistro;

Como pueden observar simplemente he declarado un registro de tres campos y luego un arreglo de siete
celdas donde cada una corresponde al registro. Si lo dibujara podríamos tener algo así:

MiLista
Nombre Nombre Nombre Nombre Nombre Nombre Nombre
Documento Documento Documento Documento Documento Documento Documento
Edad Edad Edad Edad Edad Edad Edad
1 2 3 4 5 6 7

Si cada celda es un registro, entonces para acceder al campo Nombre de la celda número 1 tendríamos lo
siguiente:
MiLista[1].Nombre

Recuerden que cada celda de un arreglo es como una variable del tipo del arreglo, por lo tanto, cada celda de
este arreglo es del tipo Registro por lo cual funciona como tal. Espero que este simple ejemplo les sirva para
visualizar esto.

Ahora les propongo un ejercicio para comprender mejor lo básico de los registros para luego continuar con un
programa más complejo:

Ejercicio 1: Determinen cuáles de las siguientes definiciones de registro son válidas:

a)

TYPE estado = RECORD


nombre : ARRAY[1..30] OF CHAR;
edad : 0..120;
sexo : (Fem, Masc);
b)

TYPE ejemplo = RECORD


prueba : 0..100;
final : 'A'..'F';
orden : 1..100
END;
c)

TYPE franco = RECORD


prueba : 0..100;
examen : 0..10;
prueba : 0..100
END;

Vladimir Rodríguez 109


Programación estructurada en Pascal

d)

TYPE califs = RECORD


nombre,
direccion : ARRAY[1..30] OF CHAR;
prueba,
examen : 0..100;
tipoest : (flojo, trabajador, puntual, impuntual)
END;

Ejercicio 2 “Programa Complejo”: En pos de comprender realmente lo que hemos aprendido hasta ahora,
les propondré ir trabajando gradualmente en el programa que describiré a continuación. Su dificultad no se comparará
con la de Master Mind, sin embargo será de gran utilidad trabajar con él. Lo programaremos de forma sencilla, o
mejor dicho, lo programarán de forma sencilla para luego ir agregando cosas. De este modo verán, además de lo que
refiere a los temas en sí, lo complicado que puede resultar agregar cosas a un programa ya escrito. Por este motivo y
por otros más, yo les iré marcando ciertas estructuras que deberán utilizar obligatoriamente más las que ustedes
mismos deseen agregar, de modo que todo sea bastante más legible que si se hace a lo bruto. Vamos a ello:

BASE DE PRODUCTOS – Parte 1

Comenzaremos a armar algo así como una base de datos de un comercio pequeño. Dicho comercio tiene a su
disposición elementos que únicamente pueden corresponderse con las siguientes categorías: Comestibles, Frutas y
Verduras, Higiene del hogar e Higiene personal. Como ven son solo cuatro.
Asimismo, para cada elemento tenemos los siguientes datos que deseamos almacenar:
• Nombre: Simplemente una palabra que identifique al producto.
• ID: Un número que es único para cada producto y lo identifica entre los demás.
• Stock: Qué cantidad de este producto tenemos a disposición.
• Precio: Cuanto vale el producto.
• Categoría: A cual de las cuatro categorías anteriores corresponde el producto.
Para comenzar supondremos que el comercio tiene únicamente cinco productos a la venta, ni más ni menos, y
que no agregará ni quitará ninguno. Nuestro programa almacenará los datos de cada producto en un arreglo de cinco
celdas, una para cada producto, cada una de las cuales almacenará todos los datos mencionados anteriormente. Los
datos de cada producto serán ingresados mediante la entrada estándar por un usuario. Asumiremos para todos los
casos que los datos ingresados son correctos, o sea que: ID es un número entero positivo y que no se repetirá para
ningún producto, que stock es un entero positivo o 0, que precio es un real positivo y que categoría se corresponde
con alguna de las anteriores.
De este modo tendremos las siguientes declaraciones obligatorias:

Const
N= 5;

Type
Categorias= (comestibles, frutas_verduras, higiene_h, higiene_p);
TNaturales= 1..MAXINT;

Elemento= Record
Nombre: String;
Stock: TNaturales;
Precio: Real;
Categoria: Categorias;
ID: TNaturales;
End;

Productos= Array[1..N] of Elemento;

Al iniciar nuestro programa debe mostrarnos un menú como el siguiente:

MENÚ PRINCIPAL:

1) Ingresar datos de productos.


2) Ver lista de productos.
3) Modificar productos.
4) Vender.

Vladimir Rodríguez 110


Programación estructurada en Pascal

5) Buscar.
6) Salir.

Opción:
Para el Menú Principal usaremos un procedimiento cuya firma será la siguiente:

PROCEDURE MenuPrincipal(VAR listaProductos: Productos);

Luego entenderán mejor el funcionamiento de esto, ya que para cada menú tendremos un procedimiento
distinto que será llamado por el procedimiento MenuPrincipal. El único parámetro allí presente define a nuestra
lista de productos, o sea, nuestro arreglos de 5 celdas.

Veamos cada menú por separado y su declaración:

1) Ingresar datos de productos: Este menú nos pedirá que ingresemos los datos de todos los productos
en nuestro arreglo (lista) sin importar si ya habían datos o no, o sea, sobrescribiremos los datos
existentes. Al seleccionar esta opción veremos una pantalla como la siguiente:

|-----------Producto 1-----------|

ID >> 1
Nombre >> Azúcar
Stock >> 10
Precio >> 50,5
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 1

Allí pueden apreciar en negro lo escrito por el programa y en azul lo que ingresa el usuario. Cada opción
aparecerá a medida que ingresamos la anterior, o sea, una vez ingresemos el valor de ID y presionemos ENTER,
veremos la opción de ingreso de Nombre. Allí ven el orden en el que se ingresan los datos. Como las categorías
fueron declaradas como un enumerado y estos no pueden leerse desde la entrada estándar, debemos dar una lista de
opciones a seleccionar por el usuario y luego en base a esta debemos asignar nosotros mismos el valor de la categoría.
Si continuáramos ingresando datos tendríamos algo como esto:

|-----------Producto 1-----------|

ID >> 1
Nombre >> Azúcar
Stock >> 10
Precio >> 50,5
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 1

|-----------Producto 2-----------|

ID >> 2
Nombre >> Harina
Stock >> 10
Precio >> 42,5
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 1

Vladimir Rodríguez 111


Programación estructurada en Pascal

|-----------Producto 3-----------|

ID >> 3
Nombre >> Desodorante
Stock >> 10
Precio >> 89,90
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 4

Y podríamos continuar así con los dos productos restantes.


Para el ingreso de estos datos usaremos un procedimiento como el siguiente:

PROCEDURE IngresoProducto(VAR listaProductos: Productos; indice: integer);

Donde indice indicará la posición en el arreglo listaProductos en la que debe agregarse el producto.
Este procedimiento lee directamente desde la entrada estándar por lo cual será él quién muestre las salidas que se ven
en los ejemplos. Tengan en cuenta que este procedimiento ingresa de a un producto en la lista en un lugar deterinado
de la misma. La opción 1 del menú modifica toda la lista; ustedes deben implementar eso usando este procedimiento.
Al terminar de ingresar todos los datos veremos el mensaje Presione ENTER para volver al menú principal...

2) Ver lista de Productos: Este menú nos mostrará en pantalla las siguientes opciones:

Ver lista de productos:

0) Ver todos
1) Comestibles
2) Frutas y verduras
3) Higiene del hogar
4) Higiene personal

9) Volver al Menú Principal

Opción >>

La primera opción de este menú nos desplegará un listado con todos los elementos de la lista y las demás solo
aquellos que se correspondan con la categoría seleccionada. En todos los casos la salida será así:

Lista de elementos seleccionados (TODOS):

Azucar ID: 1
Harina ID: 2
Manzana ID: 3
Banana ID: 4
Fideos ID: 5

Presione ENTER para volver al Menú Principal...

Lista de elementos seleccionados (FRUTAS y VERDURAS):

Manzana ID: 3
Banana ID: 4

Presione ENTER para volver al Menú Principal...

La opción 9 simplemente nos regresa al menú anterior. No deberían tener problemas con eso.
Para esto deben utilizar un procedimiento como el siguiente:

Procedure MostrarListaProductos(listaProductos: Productos; opcion: char);

donde opcion le indicará al procedimiento el criterio por el que tiene que mostrar los productos.

Vladimir Rodríguez 112


Programación estructurada en Pascal

3) Modificar Productos: Esta opción del Menú Principal nos mostrará las siguientes nueve:

Modificar Productos:

1) Todos
2) Según ID
3) Según Nombre
4) Comestibles
5) Frutas y verduras
6) Higiene del hogar
7) Higiene personal

9) Volver al Menú Principal

Opción >>

Para esto deberán implementar un procedimiento con la siguiente firma:

PROCEDURE ModificarProductos(VAR listaProductos: Productos; opcion: char);

el cual funcionará del mismo modo que MostrarListaProductos. Una vez elegida la opción de criterio según
el cual modificaremos los productos, el programa nos mostrará los datos del primer producto a modificar y luego
debajo nos pedirá que ingresemos los nuevos datos. Supongamos que seleccionamos la ocpión 1:
Modificando productos (TODOS):

Nombre: Azucar
ID: 1
Stock: 15
Precio: 50,5
Categoría: Comestibles

Ingrese los nuevos datos:

Nombre >> Azúcar


ID >> 123
Stock >> 18
Precio >> 50,5
Categorías (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4)Higiene personal

Ocpion >> 1
Luego de lo cual pasaríamos a modificar el siguiente producto, adecuado al criterio elegido en las siete
opciones disponibles. Una vez terminados todos los productos adecuados se nos mostrará el siguiente mensaje

Ha terminado de modificar los productos (TODOS).

Presione ENTER para volver al Menú Principal...

Para mostrar los datos de un producto en pantalla deben implementar un procedimiento como el siguiente:

PROCEDURE MostrarDatosProducto(listaProductos: Productos; indice: integer);

donde indice indicará el lugar en la lista donde se encuentra nuestro producto. Si se le pasa indice como valor
igual a 0 entonces el procedimiento mostrará el mensaje: No existen datos para mostrar. Cualquier otro valor debe
ser una posición existente en nuestro arreglo de elementos.
Asimismo, para buscar un producto y obtener su índice utilizarán las siguientes funciones según el criterio de
búsqueda (ID, Nombre o Categorías):

FUNCTION BuscarPorID(listaProductos: Productos; ID: integer): integer;

FUNCTION BuscarPorNombre(listaProductos: Productos; nombre: string): integer;

FUNCTION BuscarPorCategoria(listaProductos: Productos; categoria: categorias):


integer;

Vladimir Rodríguez 113


Programación estructurada en Pascal

donde cualquiera de estas funciones retornará como valor entero el índice del primer elemento de la lista que cumpla
con la condición de búsqueda. Por ejemplo, si buscamos según la categoría Comestibles, se nos retornará la posición
del primer comestible en la lista listaProductos ignorando a todos los demás. De este modo deberán
ingeniárselas para ir obteniendo todos los productos necesarios por medio de esta función. Estas funciones deben
declararse antes que el procedimiento MostrarDatosProducto, de lo contrario no podrán llamarlas dentro del
mismo. Otra opción sería declararlas de forma interna al procedimiento, pero esto limitaría su uso solo a dicho
procedimiento.
Para cualquiera de estas tres funciones, si no encuentran el producto buscado entonces retornaran el valor 0.

Cuando se quiere modificar un producto según su ID, como dicho producto será único, se pedirá al usuario
que ingrese un ID para buscar el producto, luego se mostrarán sus datos para modificarlo posteriormente:

Modificando productos (ID):

Ingrese el ID del producto a modificar: 1

Nombre: Azucar
ID: 1
Stock: 15
Precio: 50,5
Categoría: Comestibles

Ingrese los nuevos datos:

Nombre >> Azúcar


ID >> 123
Stock >> 18
Precio >> 50,5
Categorías (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4)Higiene personal

Ocpion >> 1

Ha terminado de modificar el producto con ID original 1.

Presione ENTER para regresar al Menú Principal...

Para modificar productos según su Nombre se aplicará la misma lógica que para modificarlos según su
categoría. Tanto nombre como categoría no son datos únicos, solo el ID es único para cada elemento, todos los demás
datos se pueden repetir. De este modo deben ir buscando a lo largo de toda la lista de productos e ir modificando
aquellos que resulten adecuados al criterio de modificación. Para todos los casos asumimos que se ingresan datos
correctos. Veamos algún ejemplo más:

Modificando productos (NOMBRE):

Ingrese el nombre de los productos a modificar: Azucar

Nombre: Azucar
ID: 1
Stock: 15
Precio: 50,5
Categoría: Comestibles

Ingrese los nuevos datos:

Nombre >> Azúcar


ID >> 123
Stock >> 18
Precio >> 50,5
Categorías (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4)Higiene personal

Ocpion >> 1

Vladimir Rodríguez 114


Programación estructurada en Pascal

No existen más elementos con el nombre Azucar.

Ha terminado de modificar los elementos con el nombre original Azucar.

Presione ENTER para volver al Menú Principal...

Modificando productos (COMESTIBLES):

Nombre: Azucar
ID: 1
Stock: 15
Precio: 50,5
Categoría: Comestibles

Ingrese los nuevos datos:

Nombre >> Azúcar


ID >> 123
Stock >> 18
Precio >> 50,5
Categorías (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4)Higiene personal

Ocpion >> 1

Nombre >> Harina


ID >> 2
Stock >> 10
Precio >> 42,5
Categoría: Comestibles

Ingrese los nuevos datos:

Nombre >> Harina de máiz


ID >> 17
Stock >> 16
Precio >> 40,5
Categorías (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4)Higiene personal

Ocpion >> 1

No existen más elementos con la categoría Comestibles.

Ha terminado de modificar los elementos con la categoría original Comestibles.

Presione ENTER para volver al Menú Principal...

La opción 9 simplemente nos mostrará el mensaje Presione ENTER para volver al Menú
Principal...

4) Vender: Esta opción nos pedirá el ID del producto que deseamos vender, nos mostrará sus datos
luego, para pedirnos el Stock que deseamos vender, modificando esa cantidad en los datos del producto. Veremos el
equivalente en dinero a la venta realizada para luego ver el mensaje de regreso al menú principal. Veamos un
ejemplo:

Ingrese el ID del producto que desea vender: 1

Datos del producto:

Nombre: Azucar

Vladimir Rodríguez 115


Programación estructurada en Pascal

ID: 1
Stock: 15
Precio: 50,5
Categoría: Comestibles

Ingrese la cantidad que desea vender: 10


Dinero obtenido: 505,00

Presione ENTER para volver al Menú Principal...

5) Buscar: Aquí se nos desplegarán las siguientes opciones:

Buscar productos:

1) Según ID
2) Según Nombre

9) Volver al Menú Principal

Opción >>

La primera opción nos pedirá el ID del producto que deseamos buscar y luego nos mostrará sus datos en
pantalla. La segunda opción hará lo mismo pero mediante el nombre. Si el nombre se repite en más de un producto se
nos desplegarán los datos de todos ellos en el orden en el que fueron ingresados o modificados. Veamos ejemplos:

Buscando según ID:

Ingrese el ID del producto que desea buscar: 2

Nombre : Harina
ID: 2
Stock: 10
Precio: 42,5
Categoría: Comestibles

Presione ENTER para volver al Menú Principal...

Buscando según NOMBRE:

Ingrese el nombre del/los producto/s: Manzana

Nombre: Manzana
ID: 3
Stock: 10
Precio: 30,00
Categoría: Frutas y Verduras

Fin de la lista.

Presione ENTER para volver al Menú Principal...

Buscando según NOMBRE:

Ingrese el nombre del/los producto/s: Banana

Nombre: Banana
ID: 4
Stock: 10
Precio: 37,50
Categoría: Frutas y Verduras

Nombre: Banana
ID: 24
Stock: 8
Precio: 42,5

Vladimir Rodríguez 116


Programación estructurada en Pascal

Categoría: Comestibles

Fin de la lista.

Presione ENTER para volver al Menú Principal...

Si no hay elementos que mostrar simplemente veremos el mensaje Fin de la lista para luego ver el clásico
Presione ENTER para volver al Menú Principal...

6) Salir: Esta opción simplemente terminará con la ejecución del programa, siendo la única forma de
hacerlo. Para esto usarán la función exit de pascal.

Limpiar la pantalla: Para este ejercicio puede resultar muy conveniente el hecho de borrar el contenido de la
pantalla para mostrar uno nuevo y no que lo anterior se desplace hacia arriba. Por ejemplo, para mostrar los distintos
menús, quedaría mucho más prolijo que se borre todo lo que hay en pantalla para mostrar el nuevo menú en limpio, y
no que este aparezca debajo de un montón de datos anteriores.
Para lograr esto utilizaremos una librería de pascal. Esto no se corresponde con el cometido de este manual
por lo que solo explicaré cómo limpiar la pantalla. El uso de librerías lo veremos más adelante, en un segundo manual
donde abordaremos la programación orientada a objetos, si no manejan bien todos los conceptos que hay aquí pues
simplemente no tiene sentido seguir con cosas más complejas.

¿Qué es una librería? Básicamente es una unidad de programa que consta de funciones y procedimientos que
alguien programó y puso a nuestra disposición para que podamos utilizarlas. Esto no lo entenderán muy bien ahora y
no tiene demasiado sentido que me extienda, solo quédense con la idea de que nosotros podemos especificar qué
librería queremos usar para luego poder invocar a sus funciones y procedimientos. Claro está que debemos conocer
qué parámetro de entrada necesita cada subprograma y qué nos devolverá en caso de ser una función.

Existe una librería incluida en Free Pascal llamada CRT. Esta librería nos provee de funciones para el control
de la pantalla y el teclado. De todas ellas solo nos interesa una, ClrScr, lo cual es una abreviación de Clear Screen,
que significa Limpiar Pantalla.
Para indicar las librerías que vamos a usar debemos escribir la palabra reservada USES luego de la
declaración del identificador de nuestro programa. Luego de USES escribimos los nombres de nuestras librerías y ya,
podemos comos llamar a sus funciones y procedimientos sin problema. Veamos un simple ejemplo de la limpieza de
la pantalla:
1 PROGRAM LimpiaPantalla;
2
3 USES crt;
4
5 VAR
6 i, j: integer;
7
8 BEGIN
9 For i:=1 to 20 do
10 begin
11 For j:=1 to 20 do
12 write('#');
13 writeln;
14 end;
15 writeln;
16 write('Presione ENTER para limpiar la pantalla...');
17 readln;
18 clrscr;
19 write('Ha borrado la pantalla. Presione ENTER para cerrar...');
20 readln;
21 END.

Este programa lo único que hace es dibujar un cuadro de 20x20 utilizando el carácter #, luego borra el dibujo
y muestra un nuevo mensaje. La función clrscr puede ser llamada cada vez que queramos.

Hasta aquí va esta parte del programa. En sí no cambiaremos mucho su funcionamiento, sino que
utilizaremos nuevas herramientas para optimizarlo, pero esto lo veremos a continuación. Si necesitan que corrija sus
programas, sus códigos fuente, si tienen dudas, si quieren mis propios códigos fuente del programa completo o de
alguno de los procedimientos o funciones que les propongo implementar, si necesitan un ejecutable de ejemplo para
ver todo esto en funcionamiento simplemente escríbanme a mstrvladi@hotmail.com solicitándome lo que les haga
falta y les responderé a la brevedad. Mucha suerte. Intenten hacer este programa antes de continuar, ya que me basaré
en esto para aplicar muchos de los conceptos que aún nos faltan por ver.

Vladimir Rodríguez 117


Programación estructurada en Pascal

La función WITH... DO:

Esta función es solo para aplicarse con registros y su utilidad radica casi únicamente en hacernos escribir
mucho menos ya que nos facilita el acceso a las variables de un registro. Veamos un simple ejemplo de su
funcionamiento. Primero escribiré un simple programa que dado un registro asignará valores a sus variables:

1 PROGRAM PrimeroSinWith;
2
3 TYPE
4 Grados= (primero, segundo, tercero, cuarto, quinto, sexto);
5
6 Estudiante= RECORD
7 Nombre: String;
8 Edad, Documento: Integer;
9 Grado: Grados;
10 END;
11 VAR
12 Alumno: Estudiante;
13
14 BEGIN
15 Alumno.Nombre:= 'Vladimir';
16 Alumno.Edad:= 18;
17 Alumno.Documento:= 12345
18 Alumno.Grado:= sexto;
19 END.

Ahora veamos el mismo programa pero utilizando WITH... DO:

1 PROGRAM AhoraConWith;
2
3 TYPE
4 Grados= (primero, segundo, tercero, cuarto, quinto, sexto);
5
6 Estudiante= RECORD
7 Nombre: String;
8 Edad, Documento: Integer;
9 Grado: Grados;
10 END;
11 VAR
12 Alumno: Estudiante;
13
14 BEGIN
15 With Alumno do
16 begin
17 Nombre:= 'Vladimir';
18 Edad:= 18;
19 Documento:= 12345
20 Grado:= sexto;
22 end;
23 END.

Como ven, su único cometido es evitarnos tener que escribir constantemente el nombre de nuestra variable
del tipo registro seguido de un punto para luego especificar la variable del registro a la que queremos acceder.
Utilizando WITH... DO simplemente escribimos el nombre de nuestra variable del tipo registro una única vez y luego
nos dirigimos a las variables internas directamente. Claro está que esto funciona solo dentro de los límites del BEGIN
y el END correspondientes a la función WITH, o sea, en nuestro programa accedemos directamente a las variables de
Alumno solo dentro de las líneas 16 y 22 que corresponden al bloque de WITH.
Del mismo que en estos ejemplos utilizamos los campos del registro para asignarles valores, podemos
utilizarlos dentro de un WITH para hacer cualquier cosa de las que ya conocemos.

Vladimir Rodríguez 118


Programación estructurada en Pascal

Ambigüedad con WITH... DO:

Esto solo será un simple ejemplo para que conozcan el tipo de confusiones que se puede tener si no se usa la
función WITH con cuidado:

1 PROGRAM EjemploAmbiguo;
2
3 TYPE
4 Grados= (primero, segundo, tercero, cuarto, quinto, sexto);
5
6 Estudiante= RECORD
7 Nombre: String;
8 Edad, Documento: Integer;
9 Grado: Grados;
10 END;
11 VAR
12 Alumno: Estudiante;
13 Grado: integer; //Prestar atención a esta variable.
14
15 BEGIN
16 With Alumno do
17 begin
18 Nombre:= 'Vladimir';
19 Edad:= 18;
20 Documento:= 12345
21 Grado:= 10; //Error, Grado aquí representa a Alumno.Grado
22 Grado:= sexto; //Esto sí es correcto.
23 end;
24 END.

Como ven, solo conviene utilizar WITH cuando necesitamos trabajar únicamente con todos los datos del
registro sin involucrar otros. Claro está que si no los identificadores no se repiten no hay problema. Lo que sucede en
este programa es que el registro tiene un campo llamado Grado y a su vez he declarado una variable global llamada
Grado. Esto es totalmente posible ya que en realidad no estoy repitiendo identificadores, la variable del registro en
realidad se llama Alumno.Grado y mi variable global solo Grado. Sin embargo, dentro de WITH tendremos el
problema que se ve en el ejemplo de la línea 21, ya que intentamos asignar el valor 10 a nuestra variable global, pero
para el compilador allí nos estamos refiriendo a la variable Alumno.Grado que es de tipo Grados y no puede
asumir valores enteros.

Vladimir Rodríguez 119


Programación estructurada en Pascal

Registro con variante


Esto no es en sí un nuevo tema, sino una aplicación práctica para lo que ya conocemos. Un registro con
variante suele utilizarse para representar una entidad que puede pertenecer a diferentes categorías que tienen
diferentes datos pero que a su vez existen datos comunes a todas las categorías. Como para mí no existe mejor
explicación que un ejemplo, vamos a ello:

Supongamos que dada una persona de nuestra empresa puede ser Encargado o Peón, si es encargado entonces
le asignamos un grado que puede ser Bajo, Medio o Alto, y si es peón le asignamos un rango que puede ser Novato o
Práctico. Asimismo un peón puede tener una carga horaria de entre 40 y 48 horas semanales; para un encargado no se
especifica. Para cualquier caso guardamos además el nombre y el documento del funcionario. Como pueden ver los
datos variarán según el tipo de funcionario (categoría) pero a su vez tenemos datos comunes a todos, que son el
documento y el nombre. Veamos la estructura del registro:

TYPE
Categorias= (encargado, peon);

Funcionario= Record
Nombre: String;
Documento: Integer;
Case categoria: Categorias of
encargado: (grado: (Bajo, Medio, Alto));
peon: (rango:(Novato, Practico); cargaHoraria: 40..48);
End;

Explicaré un poco esto: El registro Funcionario funciona igual que todos los demás vistos hasta ahora.
Tenemos la variable Nombre del tipo String, la variable Documento del tipo Integer y luego la variable
categoria del tipo Categorias. Como pueden ver esta variable se declara y se define en el encabezado de un
CASE... OF, cosa que hasta ahora no habíamos visto y solo utilizaremos esta forma de declarar un CASE para los
registros con variante. Dependiendo del valor que tome esta variable definiremos las siguientes:
Si categoria toma el valor encargado entonces definimos la variable grado como un
enumerado que corresponde a los grados de un encargado; si toma el valor peon entonces nos definimos
la variable rango como otro enumerado con los rangos de un peón y su vez definimos la variable
cargaHoraria como un subrango de enteros que va de 40 a 48 inclusive. Como en este caso
definimos dos variables diferentes las separamos por punto y coma. Todas las variables declaradas para
cada caso van entre paréntesis. Las variables de un mismo tipo pueden declararse todas juntas
separándolas por coma. Esto funciona como en los encabezados de un procedimiento o una función.
A pesar de darle esta forma, el registro funciona como si estuviera declarado de esta manera:

Funcionario= Record
Nombre: String;
Documento: Integer;
categoria: Categorias;
grado: (Bajo, Medio, Alto);
rango:(Novato, Practico);
cargaHoraria: 40..48);
End;

Sin embargo, darle una aplicación correcta a la estructura anterior hace que un código sea mucho más legible,
corto, entendible y no van a creer cuantos errores de programación nos ahorramos. Además, la estructura del registro
con variante nos da una visión mucho más clara de la información que estamos manejando y de cómo utilizarla.

El acceso a las variables (campos) del registro sigue siendo como siempre a pesar de que su declaración ha
cambiado, por eso mismo les he aclarado que el registro con variante funciona como si fuera este último que ven
aquí, sin embargo en este último no es posible visualizar qué tipo de información corresponde a los distintos casos
¿me comprenden? En el primero se ve claramente que si el funcionario es encargado debemos asignarle un grado, y si
es peón le asignamos un rango y una carga horaria. Declarando el registro como uno normal da la impresión de que
todos esos datos irán para todos los funcionarios, o sea, todos tienen una categoría, un grado, un rango y una carga
horaria, pero en realidad no es así.

Veamos entonces un simple programa en el que tenemos un arreglo de tres celdas el cual contendrá los datos
de los empelados en cada una de ellas, o sea, tenemos tres personas. Los datos serán leídos de la entrada estándar y
asumiremos que son correctos siempre. Utilizaré la estructura del FOR para esto ya que ingresaremos tres
funcionarios sí o sí, no me interesa nada más:

Vladimir Rodríguez 120


Programación estructurada en Pascal

1. PROGRAM RegistroVariante;
2.
3. CONST
4. N= 3;
5.
6. TYPE
7. Categorias= (encargado, peon);
8.
9. Funcionario= Record
10. Nombre: String;
11. Documento: Integer;
12. Case categoria: Categorias of
13. encargado: (grado: (Bajo, Medio, Alto));
14. peon: (rango:(Novato, Practico); cargaHoraria: 40..48);
15. End;
16.
17. Personal= Array[1..N] of Funcionario;
18.
19. VAR
20. MisFuncionarios: Personal;
21. i: integer;
22. opcion: char;
23.
24. BEGIN
25. For i:=1 to N do
26. begin
27. writeln('Funcionario número ',i,':');
28. writeln;
29. write('Nombre: ');
30. readln(MisFuncionarios[i].Nombre);
31. write('Documento: ');
32. readln(MisFuncionarios[i].Documento);
33. writeln('Seleccione una categoría: (1) Encargado');
34. writeln(' (2) Peón');
35. write('Opción: ');
36. readln(opcion);
37.
38. Case opcion of
39. '1': begin
40. writeln;
41. writeln('Seleccione un grado para el encargado: (B)
Bajo');
42. writeln(' (M)
Medio');
43. writeln(' (A)
Alto');
44. write('Opción: ');
45. readln(opcion);
46.
47. Case opcion of
48. 'b', 'B': MisFuncionarios[i].grado:= Bajo;
49. 'm', 'M': MisFuncionarios[i].grado:= Medio;
50. 'a', 'A': MisFuncionarios[i].grado:= Alto;
51. End;
52. end;
53. '2': begin
54. writeln;
55. writeln('Seleccione un rango para el peón: (N)
Novato');
56. writeln(' (P)
Práctico');
57. write('Opción: ');
58. readln(opcion);
59.
60. Case opcion of
61. 'n', 'N': MisFuncionarios[i].rango:= Novato;
62. 'p', 'P': MisFuncionarios[i].rango:= Practico;
63. End;

Vladimir Rodríguez 121


Programación estructurada en Pascal

64.
65. writeln;
66. write('Ingrese la carga horaria semanal del peón (40 a
48 hs): ');
67. readln(MisFuncionarios[i].cargaHoraria);
68. end;
69. End;
70.
71. writeln;
72. end;
73. END.

Explicaré un poco este programa, aunque no debería darles problemas ya que en sí no es nada complejo, solo
leemos y asignamos datos, nada más. En la línea 17 declaramos el tipo Personal que no es más que un arreglo del
tipo Funcionario, o sea, un arreglo donde cada celda representa a un registro. En la línea 20 declaro una variable
del tipo Personal, o sea, creo mi arreglo para trabajar; luego declaro la típica variable i del tipo integer para
iterar en las celdas del arreglo. Finalmente declaramos la variable opcion del tipo char, esta la usaremos para
asignar valores a los tipos enumerados ya que estos no pueden leerse de la entrada estándar.
El bloque principal de nuestro programa es un enorme FOR que iterará desde 1 a N en nuestro arreglo, o sea
en todas sus celdas. Como N vale 3, nuestro FOR iterará 3 veces con lo cual terminará nuestro programa. En la línea
30 leemos directamente el campo Nombre de la celda i de nuestro arreglo; hacemos lo mismo en la línea 32 para el
campo Documento. Luego desplegamos en pantalla las opciones para la categoría de nuestro funcionario i, las
cuales las leeremos como caracteres en la variable opcion. Dependiendo e la opción elegida por el usuario será la
información que pediremos. Si se ingresa un 1 entonces pediremos los datos para un encargado y se ingresa un 2
pediremos los datos para un peón. Esto lo ven con el CASE opcion OF que inicia en la línea 38 y cierra en la línea
69. El funcionamiento de esta estructura no debería darles problemas. Lo que cabe destacar aquí es que, por ejemplo,
si la opción elegida es la 1, o sea, si estamos ingresando los datos de un encargado, solo usaremos las variables que
guardan la información pertinente a esa categoría y no tocamos las demás porque no nos interesan. Lo que quiero
decir es que, por ejemplo, para un encargado no tocamos en ningún momento las variables rango o
cargaHoraria porque no nos interesan y no trabajaremos con ellas en un posterior tratamiento de la información.

Es un error asignar valores a campos que no se corresponden con la información que estamos tratando, o sea,
si vamos a trabajar con un peón entonces nos aseguramos de ello y luego asignamos los valores solo y únicamente a
las variables que son pertinentes a la categoría de peón. Podríamos, si queremos, asignar un valor de grado a pesar
de estar trabajando con la categoría peón, el compilador lo permitirá tranquilamente y no habrá ningún tipo de error
en el programa en sí, pero está mal porque un peón no tiene grado sino rango y no nos importa los valores que
grado pueda tomar en este caso.

Vladimir Rodríguez 122


Programación estructurada en Pascal

Arreglo con tope:

Esta no es una nueva estructura sino una nueva implementación de una estructura conocida, los arreglos. A
estas alturas deberían ser capaces de trabajar perfectamente con ellos. Como bien saben, los arreglos se definen de un
tamaño fijo y este no puede variar a lo largo del programa ya que es asignado en tiempo de compilación. Ahora bien,
imaginen que necesitamos almacenar datos pero que a priori no sabemos cuantos, por ejemplo, imaginen que en el
programa del supermercado no sabemos cuantos productos tendremos que ingresar, puede ser uno o pueden ser
treinta ¿cómo hacemos?
En principio utilizaremos los arreglos, luego veremos cómo se hace esto realmente utilizando memoria
dinámica, nuestro último tema. Bien, vamos a los arreglos con tope, la idea es la siguiente: Me asigno un número
máximo de objetos a almacenar, lo cual definirá la dimensión del arreglo; luego voy registrando cuántos elementos
almaceno en el mismo y será hasta ese punto donde navegaré en el arreglo, no me interesará el resto. Por ejemplo, si
nos definimos un arreglo de 50 celdas del tipo carácter, el cual usaremos para guardar palabras que pueden tener entre
una y cincuenta letras, al guardar una palabra de diez letras solo nos interesa recorrer las primeras diez celdas del
arreglo, las otras cuarenta no serán necesarias. En este caso mi tope será el número 10 y lo implementaré como si se
tratase del final de mi arreglo.

Veamos un ejemplo de esto en un código de un simple programa que espera recibir de la entrada un código
numérico de tamaño variable con un máximo de 20 caracteres. La idea es verificar que efectivamente el código sea
numérico. No podemos leer la entrada estándar como un entero porque si el usuario pone otra cosa que no sean
números el programa dará un error en tiempo de ejecución, nada recomendable. Lo mismo sucede con el tipo real. Si
leemos la entrada como string no tendremos problemas en ejecución, sin embargo ¿cómo verificamos que la cadena
ingresada sea justamente numérica?

1. PROGRAM ArrayConTope;
2.
3. CONST
4. MaxCaracteres= 20;
5.
6. TYPE
7. Codigo= Array[1..MaxCaracteres] of char;
8.
9. VAR
10. MiCodigo: Codigo;
11. Tope, i: integer;
12. error: boolean;
13.
14.
15. BEGIN
16. Tope:= 0; //Representa un código (palabra) nulo ya que no existe celda
de índice 0.
17. write('Ingrese un código numérico: '); //Mensaje para el usuario.
18.
19. //Leemos hasta el fin de línea o hasta llegar al máximo permitido.
20. //Notar que leemos directamente sobre la celda del arreglo.
21. Repeat
22. Tope:= Tope + 1;
23. read(MiCodigo[Tope]);
24. Until (eoln) or (Tope=MaxCaracteres);
25.
26. readln; //Consumimos el fin de línea.
27.
28. //Iteramos por nuestro arreglo hasta que exista un caracter no numérico
29. //o hasta llegar al tope sin haber encontrado error.
30. i:=0;
31. Repeat
32. i:= i + 1;
33. error:= (ord(MiCodigo[i])<ord('0')) or (ord(MiCodigo[i])>ord('9'));
34. Until (error) or (i=Tope);
35.
36. If error then
37. writeln('Error en código, no es numérico.')
38. else
39. writeln('Código correcto.');
40. END.

Vladimir Rodríguez 123


Programación estructurada en Pascal

Podríamos decir que el programa anterior no está mal ya que aplica la estructura del arreglo con tope
correctamente, sin embargo no es la implementación más adecuada (esto dependerá del punto de vista de los
programadores). Dado que el arreglo con tope en realidad está representando un conjunto, el cual puede ser vacío
cuando el tope vale 0 o puede contener elementos variables a lo largo del programa, la implementación correcta es
aquella que lo visualiza como una única entidad. De este modo, la forma correcta de aplicar el programa anterior es la
siguiente:
1. PROGRAM ArrayConTope;
2.
3. CONST
4. MaxCaracteres= 20;
5.
6. TYPE
7. ConjuntoCodigo= Record
8. Codigo: Array[1..MaxCaracteres] of char;
9. Tope: 0..MaxCaracteres;
10. End;
11.
12. VAR
13. MiCodigo: ConjuntoCodigo;
14. i: integer;
15. error: boolean;
16.
17.
18. BEGIN
19. MiCodigo.Tope:= 0; //Representa un código (palabra) nulo ya que no
existe celda de índice 0.
20. write('Ingrese un código numérico: '); //Mensaje para el usuario.
21.
22. //Leemos hasta el fin de línea o hasta llegar al máximo permitido.
23. //Notar que leemos directamente sobre la celda del arreglo.
24. Repeat
25. MiCodigo.Tope:= MiCodigo.Tope + 1;
26. read(MiCodigo.Codigo[MiCodigo.Tope]);
27. Until (eoln) or (MiCodigo.Tope=MaxCaracteres);
28.
29. readln; //Consumimos el fin de línea.
30.
31. //Iteramos por nuestro arreglo hasta que exista un caracter no
numérico
32. //o hasta llegar al tope sin haber encontrado error.
33. i:=0;
34. Repeat
35. i:= i + 1;
36. error:= (ord(MiCodigo.Codigo[i])<ord('0')) or
(ord(MiCodigo.Codigo[i])>ord('9'));
37. Until (error) or (i=MiCodigo.Tope);
38.
39. If error then
40. writeln('Error en código, no es numérico.')
41. else
42. writeln('Código correcto.');
43. END.

Ejercicio 0: Modifiquen este programa para utilizarlo con WITH...DO.

A continuación les expongo varios ejercicios, todos diseñados por docentes de Facultad de Ingeniería. Todos
los ejercicios pueden y deben ser resueltos con lo visto hasta el momento. La información que yo les he dado en este
manual resulta incluso mucho más completa que la que dan los profesores en las clases ya que ellos apuntan a que los
estudiantes investiguen lo que no conocen y se las arreglen solos en muchos casos, usando clases de consulta como
apoyo a las dudas. Ustedes pueden escribirme a mi correo electrónico mstrvladi@hotmail.com para consultar todas
las dudas. Muchos ejercicios son de gran dificultad, identificaré las dificultades de cada ejercicio con colores: Verde
(dificultad baja), Amarillo (Dificultad Media), Naranja (Dificultad Media Alta), Rojo (Dificultad Alta).
Algunos ejercicios apuntan, además de a la programación en sí, al uso de conocimientos matemáticos que no
se imparten aquí, por lo cual simplemente aclararé para esos casos que se requiere el conocimiento matemático y
ustedes decidirán si lo tienen o no para realizar el ejercicio. Fuera de todos aquellos que requieren matemática, usedes
deben ser capaces de resolver todos estos ejercicios. Recuerden que la mejor herramienta para la programación es la
práctica. Si no se ponen con estas cosas no llegarán a nada, y tratar de avanzar para ver que viene pero dejando cosas
en el camino no servirá de nada. ¡¡¡Adelante!!!

Vladimir Rodríguez 124


Programación estructurada en Pascal

Ejercicio 1: Examinen la siguiente declaración :

CONST
CANT_PERS = 100;
MAX_CADENA = 30;
TYPE
Cadena = RECORD
letras : ARRAY[1..MAX_CADENA] OF CHAR;
largo : 0..MAX_CADENA;
END;
Persona = RECORD
nombre : Cadena;
edad : 0..120;
estado : (casado, soltero, divorciado);
salario : REAL;
exenciones : 0..MAXINT;
END;
VAR
juanita : Persona;
grupo : ARRAY[1..CANT_PERS] OF Persona;

Determinen cuáles de las siguientes proposiciones son válidas:

a)
grupo [1] := juanita;
b)
grupo [1].nombre := 'juanita';
c)
Read (grupo [1].estado);
d)
WITH grupo DO writeln (nombre);
e)
WITH grupo [100] DO
BEGIN
Read (edad);
END;
f)
WITH juanita DO
BEGIN
nombre := grupo [50].nombre;
salario := grupo [1].salario
END;

Ejercicio 2: Supongan que una sociedad genealógica tiene un arreglo de registros que indican la fecha de
nacimiento y subíndice en el arreglo de los registros de los padres y madres de varias personas. El arreglo de registros
y las variables correspondientes podrían ser parecidos a éstos :

Vladimir Rodríguez 125


Programación estructurada en Pascal

CONST

MAXPERSONAS = 1000;
MAXCAD = 20;

TYPE
Cadena = RECORD
letras : Array[1..MAXCAD] Of Char;
largo : 0..MAXCAD;
END;
UnaPersona = RECORD
nombre : Cadena;
FechNac : RECORD (* Fecha de nacimiento *)
mes : 1..12;
dia : 1..31;
anio : 0..MAXINT
END;
IndMadre, IndPadre : 0..MAXPERSONAS;
END;
Familia = RECORD
pers : ARRAY[1..MAXPERSONAS] OF UnaPersona;
tope : 0..MAXPERSONAS;
END;
VAR
historia : Familia;
usted : Cadena;

Los campos IndMadre e IndPadre contienen el subíndice en el arreglo historia de los registros de la madre
y el padre. Este valor será cero si no se dispone de la información correspondiente. Supongan que la variable
usted contiene el nombre de una persona; exhiban entonces los nombres y fechas de nacimiento de los padres
y los cuatro abuelos de esa persona (si es que se contienen la información).

Ejercicio 3: Escriba una función en PASCAL llamada alfa que tenga como parámetro una cadena de
caracteres (representada con un arreglo con tope) llamada frase y una variable de tipo carácter llamada letra. La
función alfa produce el número de apariciones del carácter letra en la frase.

Ejercicio 4: (Requiere conocimientos de matemática básica de nivel secundario, concretamente deben saber
hallar las raíces de cualquier polinomio de segundo grado (función cuadrática)) Implementen un procedimiento que
calcule las raíces de una ecuación de segundo grado. El cabezal del procedimiento debe de ser el siguiente:

Procedure Raices(a, b, c : Real; Var r : TipoRaices);

1. Definan el tipo TipoRaices utilizando la estructura de registro con variante.


2. Implementen el procedimiento pedido.

Ejercicio 5: (Este conocimiento está basado en una notación matemática común para cualquier estudiante
pero avanzada para su tratamiento. El conocimiento necesario se corresponde con los últimos años de secundaria y/o
inicios de estudios terciarios) Se considera el tipo de dato Nerr que es la unión de los números naturales (N) con el
conjunto Err. El conjunto Err se define como {diverr, reserr, argerr}, donde diverr es el error de la
división por cero,reserr es el error de la resta con resultado negativo y argerr es el error de cualquier operación
donde alguno de sus argumentos no es natural.
Se definen las siguientes operaciones sobre elementos del tipo Nerr:

Vladimir Rodríguez 126


Programación estructurada en Pascal

division: Nerr x Nerr → Nerr


Si a pertenece a N y b pertenece a N - {0} ==> divivision(a,b) = a DIV b;
Si a pertenece a N y b = 0 ==> division(a,b) = diverr;
Si a pertenece a Err o b pertenece a Err ==> division(a,b) = argerr;

resta: Nerr x Nerr → Nerr


Si a pertenece a N y b pertenece a N y a >= b ==> resta(a,b) = a - b;
Si a pertenece a N y b pertenece a N y a < b ==> resta(a,b) = reserr;
Si a pertenece a Err o b pertenece a Err ==> resta(a,b) = argerr;

suma: Nerr x Nerr → Nerr


Si a pertenece a N y b pertenece a N ==> suma(a,b) = a + b;
Si a pertenece a Err o b pertenece a Err ==> suma(a,b) = argerr;

producto: Nerr x Nerr → Nerr


Si a pertenece a N y b pertenece a N ==> producto(a,b) = a * b;
Si a pertenece a Err o b pertenece a Err ==> producto(a,b) = argerr;

1. Definan en PASCAL el tipo Err.

2. Definan en PASCAL el tipo Nerr.

3. Implementen los procedimientos que implementan respectivamente las operaciones división, resta,
suma y producto del tipo Nerr.

PROCEDURE division (n1:Nerr; n2:Nerr; VAR n3:Nerr);


PROCEDURE resta (n1:Nerr; n2:Nerr; VAR n3:Nerr);
PROCEDURE suma (n1:Nerr; n2:Nerr; VAR n3:Nerr);
PROCEDURE producto (n1:Nerr; n2:Nerr; VAR n3:Nerr);

Ejercicio 6: (Matemática de nivel terciario (Álgebra lineal de matrices) Se desea implementar en PASCAL el
producto de matrices de elementos de tipo Nerr. Las matrices a operar podrán tener diferentes dimensiones. Se sabe
que si una matriz tiene dimensión mxn, m y n son enteros positivos que nunca superarán a constantes X e Y
respectivamente.
El producto entre matrices de tipo Nerr se define de manera análoga al producto de matrices de números
naturales con la suma y el producto para el tipo Nerr dado en el ejercicio anterior.
Sea la matriz m1 de dimensión m x n y la matriz m2 de dimensión p x q. Si n = p el producto m1 x m2 tendrá
dimensión m x q, en caso contrario diremos que el producto falla.
Definir en PASCAL el tipo MNerr, que representa las matrices de dimensiones m x n de tipo Nerr, para
cualquier m entre 1 e Y, y para cualquier n entre 1 y X. Puede asumir que X e Y son mayores o iguales que 1.
Además MNerr debe tener un valor de error “merr” para el caso en que el producto de matrices falle.
Implementar el procedimiento:

PROCEDURE mprod (m1:MNErr; m2:MNerr; VAR m3:MNerr);

El procedimiento mprod recibe dos matrices m1 y m2, retornando en m3 el producto m1 x m2 o merren caso
de que dicho producto falle.

Ejercicio 7: En una isla del Caribe una banda de piratas se gana la vida asaltando barcos mercantes.
Anualmente en la isla se realiza un evento multitudinario llamado “la entrega de los premios Calavera”. Para este año
los piratas están pensando en entregar el premio “Calavera de Oro” al pirata que haya conseguido mas dinero
asaltando barcos para la banda. Como usualmente los piratas discuten a quién le corresponden los premios en
trifulcas interminables y sangrientas, este año la comisión directiva de la banda ha decidido informatizar el registro de
logros de los piratas en los distintos asaltos, con la esperanza de terminar así con algunas de las discuciones sobre los
créditos que le corresponden a cada pirata.

Los logros de los piratas durante el año se organizan en la siguiente estructura:

Vladimir Rodríguez 127


Programación estructurada en Pascal

CONST
MAXPIRATAS=10000; (* Limite de piratas que pueden vivir en la isla. *)
MAXASALTOS=500; (* Limite de asaltos a los que estadisticamente puede sobrevivir
un pirata. *)
MAXDIGITOSCI=8; (* Cantidad de digitos para almacenar un numero de cédula. *)

TYPE
TipoCI=ARRAY[1..MAXDIGITOSCI] OF CHAR;

TipoFecha=RECORD
dia:1..31;
mes:1..12;
anho:INTEGER
END;

TipoAsalto=RECORD
nombre_barco:ARRAY[1..30] OF CHAR; (* Nombre del barco asaltado *)
fecha:TipoFecha; (* Fecha del atraco *)
botin:INTEGER (* Suma de dinero obtenida para la
banda *)
END;

ConjuntoAsaltos=RECORD (* Contiene información sobre los asaltos *)


asaltos:ARRAY[1..MAXASALTOS] OF TipoAsalto;
tope:0..MAXASALTOS
END;

TipoPirata=RECORD
nombre:ARRAY[1..30] OF CHAR; (* Nombre del pirata *)
CI:TipoCI; (* Cédula de identidad *)
CASE estavivo:BOOLEAN OF (* Indica el estado vital actual del
pirata *)
TRUE: (asaltos:ConjuntoAsaltos); (* Contiene información sobre los
asaltos *)
(* realizados por el pirata
*)
FALSE: ()
END;

Banda=RECORD
pirata:ARRAY[1..MAXPIRATAS] OF TipoPirata; (* Contiene información de los
piratas de la banda *)
tope:0..MAXPIRATAS (* Indica cantidad de piratas
*)
END;

Se pide:

Parte a)
Implementar la función:

FUNCTION dinero_obtenido_por_pirata(pirata:TipoCI, anho:INTEGER, b:Banda) :


INTEGER;

la cual debe recibir la CI de un pirata, un año y una banda, y retornar la suma de dinero obtenida por el pirata
para la banda en ese año. En caso de que el pirata se encuentre muerto o no se encuentre en la banda debe
retornar 0.

Se sugiere implementar primero funciones:

Vladimir Rodríguez 128


Programación estructurada en Pascal

FUNCTION CIiguales (ci1, ci2:TipoCI):BOOLEAN;


(* Retorna TRUE si "ci1" y "ci2" son iguales, FALSE si no. *)

FUNCTION contar_dinero (ca:ConjuntoAsaltos; anho:INTEGER):INTEGER;


(* Retorna la suma del dinero obtenido en los asaltos del conjunto "ca"
realizados durante el año "anho". *)

Parte b)
Implementar la función:

PROCEDURE hallar_ganadores(piratas:Banda, anho:INTEGER; VAR


piratas_merecedores:ConjuntoCIs)

la cual dada una banda de pirata y un año, devuelve en piratas_merecedores las cédulas de los piratas vivos
merecedores del premio “Calavera de Oro”. Tengamos en cuenta, que varios piratas pueden coincidir en la
cantidad de dinero obtenido para la banda y que no hayan otros piratas que los superen. ConjuntoCIs se declara
como sigue:

TYPE
ConjuntoCIs=RECORD
cedulas:ARRAY[1..MAXPIRATAS] OF TipoCI; (* Arreglo de
cedulas *)
tope:0..MAXPIRATAS (* Cantidad de
cédulas en el array *)
END;

Ejercicio 8: Examen Agosto 2001

Se desea trabajar con una aritmética de Naturales de hasta 100 dígitos. Los Enteros de PASCAL no soportan
dicha aritmética, por lo que se piensa utilizar la siguiente representación de Naturales basada en arreglos con
tope:

CONST
MaxDig = 100;
TYPE
Digito = 0..9;
Natural = RECORD
digitos : ARRAY[1..MaxDig] OF Digito;
tope : 0..MaxDig;
END;

Implementar la suma de Naturales representados en términos de la estructura anterior. Utilizar el siguiente


cabezal:

Procedure Suma(a, b : Natural: Var c : Natural);

Vladimir Rodríguez 129


Programación estructurada en Pascal

BASE PRODUCTOS – Parte 2

Añadiremos dos funcionalidades más a nuestro programa: Captura de Errores y una Lista Variable de
Elementos a almacenar. Para esto tendrán que modificar bastante su código, lo cual puede ser bastante complicado y
les mostrará la realidad de trabajar con un código ya hecho. Si no utilizaron buenos comentarios estarán en problemas
ya que tendrán que rememorar lo que hace su programa e incluso con ellos tendrán que acomodar su cabeza
nuevamente a una lógica puntual. Cada programa es un mundo, tiene sus problemas y complicaciones, su estructura;
y para cada uno deben adecuar su mente. Imaginen lo que es trabajar con el código de otra persona cuya cabeza
funciona de una forma totalmente diferente a la nuestra, cuya lógica nos hace rompernos la cabeza en el intento por
comprendela. Pues ese es el mundo de la programación, de la creación de software, y créanme, lo que yo les he
mostrado en este manual no es nada. En próximas entregas iremos sumergiéndonos más en un mundo donde la magia
es posible, donde la imaginación es prácticamente el único límite, donde el ingenio es la mejor herramienta, donde la
paciencia es la mejor virtud. Sin embargo nada será sencillo, necesitarán sentarse a pensar horas cómo lograr tal o
cual cosa, analizar una y otra vez dónde cornos está el error que hace a nuestro programa tirarnos cualquier cosa en
un caso concreto ¡¡¡¡PERO SI EN TODOS LOS DEMÁS CASOS FUNCIONA!!! Muchas veces pensarán que no
hay solución, que necesitan alguna otra herramienta, sin embargo este manual ha apuntado a que ustedes puedan
lograr grandes soluciones con herramientas básicas. PASCAL tiene muchas cosas más que nosotros ni hemos
nombrado y que en ciertos aspectos solucionarían muchos problemas de forma automática. Lenguajes superiores
lograrán con dos palabras resolver lo que a ustedes les ha llevado diez o más líneas de código y horas de rompedero
de cabeza, pero así se aprende y así, con esos lenguajes superiores, lograrán resolver cosas que tal vez otras personas
no podrían. Bien, vayamos a nuestro programa.

Nuestro programa deberá tiranos el mensaje “ERROR: Dato inesperado, ingrese un dato válido” para
cualquier caso en el que ingresemos algo inadecuado en cualquier entrada. Siempre que se muestre el mensaje de
error el programa deberá quedar a la espera de un nuevo ingreso para el mismo dato. Detallaré aquí los posibles
errores en las entradas y daré ejemplos de ejecuciones:

• Nombre: El nombre de un producto debe estar formado únicamente por números y/o letras
mayúsculas y/o minúsculas. Cualquier otro carácter se tomará como inválido.
• ID: El ID debe ser un número entero positivo, o sea, un natural. Si el dato ingresado no se
corresponde con un número entero positivo o directamente no es un número lo consideraremos
como error. Para el ID también deberemos corroborar que no se repita en todos los ingresos
anteriores, ya que eso también sería un error.
• Stock: Debe ser un número entero positivo. Tiene las mismas restricciones que el ID con la
excepción de Stock sí puede repetirse.
• Precio: Este dato debe ser un número real positivo, en caso de ser negativo o no ser un número lo
consideraremos como error. Este dato les traerá problemas de captura ya que leer la entrada como
carácter y luego pasarla a real no es tan sencillo como pasarla a entero.
• Categoría: Para este dato solo debemos corroborar que lo ingresado se corresponda con una de las
opciones mostradas en pantalla, de lo contrario será un error. Esto se aplicará a todas las pantallas
de nuestro programa en las que el usuario deba elegir una de varias opciones.

Para controlar el nombre ingresado deben implementar una función como la siguiente:

FUNCTION NombreCorrecto(nombre: CadenaChar): Boolean;

donde CadenaChar será un tipo global declarado de la siguiente manera:

CONST
MaxLargoNombre: 20;
TYPE
CadenaChar= RECORD
nombre: Array[1..MaxLargoNombre] of Char;
tope: 0..MaxLargoNombre;
END;

La función NombreCorrecto devolverá TRUE si la cadena ingresada cumple las condiciones de más arriba,
FALSE en caso contrario.

Vladimir Rodríguez 130


Programación estructurada en Pascal

Para corroborar que la entrada corresponde a un entero positivo deben utilizar la siguiente función:

FUNCTION EsEnteroPositivo(numero: CadenaChar): Boolean

la cual devolverá TRUE si la entrada es un entero positivo o 0. Esto lo usarán para ID y para Stock, y tal vez para
algún otro caso más donde ustedes crean que sea pertinente. No admitimos el signo de + en la entrada.

Del mismo modo comprobaremos si la entrada se corresponde a un real positivo:

FUNCITION EsRealPositivo(numero: CadenaChar): Boolean;

la cual obviamente devolverá TRUE si la entrada es un real positivo o FALSE en caso contrario. Recuerden que
tomamos como real positivo que contenga números y/o una sola coma. No admitimos el signo de + en la entrada. De
este modo, entradas correctas serían las siguientes:
• 1 Recordar que los números enteros son reales.
• 10
• 10,0
• ,12 equivale a 0,12 por lo cual es correcto.
• 1325,123

Entradas incorrectas serían:


• +1
• +1,23
• -1,23
• -0
• +0
• 12,12,3
• ,0,1
• 12,-1

Para obtener el valor entero de una entrada correcta deben implementar la función siguiente:

FUNCTION StringToInt(numero: CadenaChar): Integer;

la cual devolverá dicho valor. Esta función recibe una cadena de caracteres correcta, por lo cual la verificación de
error debe hacerse antes de su llamada.

Del mismo modo implementarán una función para obtener el valor real de una cadena de caracteres
correctos:

FUNCTION RealToInt(numero: CadenaChar): Real;

Esta función, como dije antes, les traerá problemas porque no es lo mismo pasar una cadena de caracteres a un valor
entero que a un valor real. Les explicaré la lógica que se me ocurrió a mí para su implementación:

Un número real está constituido de dos partes, la que está antes de la coma y la que está después, o sea, la
parte entera y la parte real. Por ejemplo:

• 1 : Parte entera 1, parte real 0.


• 1,000: Parte entera 1, parte real 0.
• 01,0: Parte entera 1, parte real 0.
• 12,52: Parte entera 12, parte real 52.

El truco está entonces en obtener estas dos partes por separado como números enteros, de este modo si
tenemos el 12,52 tendremos por un lado el 12 y por otro lado el 52, ambas partes como enteros, lo cual es fácil llevar
a valor integer con la función StringToInt. Una vez tenemos el entero 12 por un lado y el entero 52 por otro
debemos establecer el 52 como el real 0,52 y sumárselo al 12 con lo cual obtenemos 12,52.
12 + 0,52= 12,52.

Vladimir Rodríguez 131


Programación estructurada en Pascal

Para obtener el 0,52 a partir de 52 basta con dividirlo entre 100. De este modo, si nuestra parte real tiene un
solo dígito debemos dividirla entre 10, si tiene dos dígitos debemos dividirla entre 100, si tiene tres entre 1000 y así
sucesivamente. Por ejemplo:

• 123,146= 123 + 146/1000


• 1,2365= 1 + 2365/10000
• 01,0= 1 + 0/10

Espero que con esto puedan implementar correctamente la función RealToInt. Cualquier duda estoy a su disposición
en mi correo mstrvladi@hotmail.com.

Para comprobar que la opción de la categoría seleccionada es correcta, o para comprobar que la
opción de un menú se corresponde con una de las disponibles deben implementar una función como la siguiente:

FUNCTION OpcionCorrecta(inicioRango, finRango: char): Boolean;

la cual recibirá en sus parámetros el inicio del rango de opciones y el final del mismo, por ejemplo, viendo un menú
como los que tiene nuestro programa:

Modificar Productos:

1) Todos
2) Según ID
3) Según Nombre
4) Comestibles
5) Frutas y verduras
6) Higiene del hogar
7) Higiene personal

9) Volver al Menú Principal

Opción >>

inicioRango sería '1' y finRango sería '9'. Ahora bien, si son obseravdores deberían preguntarse qué pasa si el
usuario ingresa el valor '8'. Pues sería una buena pregunta. Para estos casos debemos modificar nuestro menú de
modo que todas las opciones sean consecutivas:

Modificar Productos:

1) Todos
2) Según ID
3) Según Nombre
4) Comestibles
5) Frutas y verduras
6) Higiene del hogar
7) Higiene personal

8) Volver al Menú Principal

Opción >>

Estas modificaciones deberán hacerlas con todos los menús que hagan falta. Asumiremos siempre que
inicioRango<finRango.

Finalmente queda por ver el hecho de que la lista de elementos sea variable. Para esto implementaremos un
arreglo con tope de la siguiente manera:

Vladimir Rodríguez 132


Programación estructurada en Pascal

Const
N= 5;

Type
Categorias= (comestibles, frutas_verduras, higiene_h, higiene_p);
TNaturales= 1..MAXINT;

Elemento= Record
Nombre: String;
Stock: TNaturales;
Precio: Real;
Categoria: Categorias;
ID: TNaturales;
End;

Productos= Record
Array[1..N] of Elemento;
tope: 0..N;
End;

Como ven, esta definición no cambia a la anterior en casi nada, simplemente implementa el tipo Productos
como un arreglo con tope. De este modo nuestro Menú Principal será así:

MENÚ PRINCIPAL:

1) Ingresar nuevo producto.


2) Ver lista de productos.
3) Modificar productos.
4) Vender.
5) Buscar.
6) Cantidad de productos ingresados.
7) Eliminar producto.
8) Salir.

Opción:

donde cambia la primera opción, ya que ahora no ingresaremos un número de productos preestablecido, sino que
podemos ingresar uno o más; y añadimos dos opciones nuevas. Veamos entonces la opción 1 de nuestro menú:

|-----------Producto 1-----------|

ID >> 1
Nombre >> Azúcar
Stock >> 10
Precio >> 50,5
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 1

Ingresar otro producto (1= SI/2= NO): j


ERROR: Dato inesperado, ingrese un dato válido.
Ingresar otro producto (1= SI/2= NO): 1

|-----------Producto 2-----------|

ID >> 2
Nombre >> Harina
Stock >> 10
Precio >> 42,5
Categorías: (1) Comestibles
(2) Frutas y verduras

Vladimir Rodríguez 133


Programación estructurada en Pascal

(3) Higiene del hogar


(4) Higiene personal

Ingrese la opción deseada >> 0


ERROR: Dato inesperado, ingrese un dato válido.
Ingrese la opción deseada >> 1
Ingresar otro producto (1= SI/2= NO): 2

Con eso volveríamos al Menú Principal. Si el usuario está en el menú principal y selecciona la opción 1,
entonces volveremos al ingreso de productos tal como se vio arriba. Imaginen que ya se han ingresado 5 productos,
entonces veríamos:

|-----------Producto 6-----------|

ID >> 6
Nombre >> AzúcarImpalpable
Stock >> 10
Precio >> 55,5
Categorías: (1) Comestibles
(2) Frutas y verduras
(3) Higiene del hogar
(4) Higiene personal

Ingrese la opción deseada >> 1

Ingresar otro producto (1= SI/2= NO): 2

Siempre debemos saber cuantos productos tenemos en nuestra lista.

Veamos ahora la opción 6: Cantidad de productos ingresados:

Simplemente se nos mostrará en pantalla la cantidad de productos en nuestra lista. Por ejemplo:

Total de productos: 6
Presione ENTER para volver al Menú Principal...

Lo último por ver es la opción Eliminar producto: Se nos mostrará el siguiente menú:

ELIMINAR PRODUCTO:

1) Eliminar según ID
2) Eliminar Comestibles
3) Eliminar Frutas y Verduras
4) Eliminar Higiene del Hogar
5) Eliminar Higiene Personal

6) Volver al Menú Principal

Opción:

Podemos eliminar un elemento determinado dando su ID, o podemos eliminar toda una categoría completa
para agilizar las cosas. Veamos dos ejemplos:

Supongamos que seleccionamos la opción 1, luego se nos pedirá el ID. Supondremos que el usuario ingresa
un ID que no existe, lo cual es un error, se pedirá de nuevo el dato, tras lo cual el usuario ingresará un ID correcto.
Como podrán ver, para no eliminar nada y volver al menú principal debe ingresarse la letra M, cualquier otra cosa
será un error.

Ingrese el ID del producto a eliminar o M para volver al menú principal: 12


ERROR: Dato inesperado, ingrese un dato válido.

Vladimir Rodríguez 134


Programación estructurada en Pascal

Ingrese el ID del producto a eliminar o M para volver al Menú Principal: 1


ID: 1
Nombre: Azúcar
Stock: 10
Precio: 50,5
Categoría: Comestibles

Seguro que desea eliminar este producto (S/N): S


Producto eliminado, presione ENTER para volver al Menú Principal...

Veamos ahora un ejemplo en el que se selecciona la categoría comestibles para eliminar los productos:

¿Seguro que desea eliminar la categoría comestibles? (S/N): S

Eliminados:

Azucar ID: 1
Harina ID: 2
Fideos ID: 5

Presione ENTER para volver al Menú Principal...

Como ven, se mostrará un listado de todos los elementos eliminados. Estarán mostrados en el orden que
fueron ingresados. Para cualquiera de estas opciones deberán implementar el siguiente procedimiento:

PROCEDURE EliminarProducto(VAR Lista: Productos; indice: integer);

donde lista será nuestro listado de elementos e indice será el lugar de la lista donde está el elemento a eliminar.
Cada vez que se elimina un elemento debemos correr todos los siguientes a ese un lugar a la izquierda y reducir el
tope un número. Veamos una ilustración:

Producto Producto Producto Producto Producto Vacío Vacío


1 2 3 4 5

1 2 3 4 5 6 7
Allí vemos una lista de elementos donde el arreglo tiene 7 celdas,
5 de las cuales contienen productos, las otras dos no, por lo tanto nuestro TOPE= 5
tope es 5. En este ejemplo eliminaremos el producto número 3, con lo
cual nuestro arreglo quedaría así.

Producto Producto Producto Producto Vacío Vacío Vacío


1 2 4 5

1 2 3 4 5 6 7
El producto número 3 fue eliminado
con lo cual ahora el producto 4 y 5 se corren un
lugar hacia la izquierda y el tope se reduce en 1, o sea, TOPE= 4
Producto 4 estaba en la celda 4 pero ahora está en la
celda 3, Producto 5 estaba en la celda 5 pero ahora está en la 4, y donde estaba Producto 5 no hay nada que nos
interese, por lo tanto corremos nuestro tope hacia la última celda que contiene información.
Nuestro procedimiento debe hacer justamente esta tarea cada vez que se elimina un elemento de nuestra lista.
Si por ejemplo se eliminaran todos los Productos menos el Producto 5 entonces quedaría así, donde TOPE= 1:

Producto Vacío Vacío Vacío Vacío Vacío Vacío


5

1 2 3 4 5 6 7

Vladimir Rodríguez 135


Programación estructurada en Pascal

Si se eliminara todo, nuestro TOPE sería 0 y nuestro arreglo estaría “vacío”, y lo pongo entre comillas porque
en realidad las celdas siguen conteniendo información solo que a nosotros no nos importa en absoluto y la tratamos
como nula o vacía, de eso se trata la implementación de Arreglos con Tope.

Si se intenta eliminar productos cuando ya no existe nada para eliminar el programa debe darnos el mensaje
“La lista de elementos está vacía”.

Hasta aquí llegamos por ahora con nuestro proyecto. La siguiente parte de este tutorial trata el último tema y,
a mi gusto, el más interesante, con lo cual modificaremos por última vez nuestro programa de modo que quede
“perfecto” y utilice solo los recursos que necesita para funcionar, pero ya veremos esto.

Deben intentar realizar todo lo que detallo aquí antes de continuar, no importa cuanto tiempo les lleve,
cuantas veces se tranquen, si tienen que escribirme a mí o buscar más info en Internet, deben lograr hacerlo. No hay
nada aquí que no puedan hacer con lo que saben hasta ahora, es más, todo está pensado para que lo hagan justamente
con estas herramientas. Si han logrado resolver la mayoría de los ejercicios anteriores más este programa, entonces
han aprendido lo básico de la programación de forma excelente y podrán resolver cualquier tipo de situación que no
requiera estructuras mucho más complejas que estas que hemos visto más las que veremos a continuación. Existe una
herramienta de implementación de funciones llamada RECURSIÓN, sin embargo no la veremos en esta entrega.

Vladimir Rodríguez 136


Programación estructurada en Pascal

SEXTA PARTE

Memoria Dinámica: El tipo Puntero

Listas Encadenadas Simples

Vladimir Rodríguez 137


Programación estructurada en Pascal

Introducción:

Hemos llegado al fin al tema más importante de este tutorial, el que he venido nombrando desde hace tiempo
y el que tal vez les haga quemar bastante más las neuronas, los Punteros. Antes que nada déjenme aclarar que el tipo
Puntero no tiene nada que ver con el puntero del ratón.... jeje... Bueno, pongámonos serios.
Como dije en la introducción a la tercera parte de este tutorial, todos los tipos que hemos visto hasta ahora son
estáticos, o sea que una vez declaradas las variables de cierto tipo se asignará en tiempo de compilación la memoria
RAM necesaria para guardar el mayor valor de dicho tipo y mantendremos siempre esa memoria reservada para
nuestro uso, la utilicemos o no. Un ejemplo bien sencillo son los arreglos con tope. Declarado un arreglo de N
cantidad de celdas, al compilar el programa se dirá que reservemos en memoria el espacio suficiente para guardar
datos en todas ellas. Supongan que N es igual a 1000 en un arreglo con tope para guardar quién sabe qué cosa; al
compilar el programa se asignará memoria para mil celdas aunque usemos solo una o dos, lo cual es un desperdicio.
Es aquí donde entra en juego el tipo Punteros, ya que nos permitirá pedir memoria en tiempo de ejecución, tanta
como necesitemos, y devolverla cuando queramos. Por eso llamamos a este tema Memoria Dinámica.

Utilizando la estructura de los punteros, por ejemplo, podemos hacer que nuestro programa de productos de
almacén pida memoria para guardar los datos de un producto cada vez que el usuario ingresa uno nuevo, y liberar
dicha memoria cuando se eliminan los productos. Ese tipo de tareas lo haremos con una estructura llamada Listas
Encadenadas, pero aún nos faltan datos para llegar a ello, así que vallamos allá con los punteros.

Vladimir Rodríguez 138


Programación estructurada en Pascal

El tipo Puntero:

Un puntero es un tipo, al igual que un arreglo o un registro, pero es un tipo muy especial ya que hace
referencia directa a un lugar específico de la memoria RAM pero no es parte de ese lugar, o sea, una variable del tipo
puntero es entonces un apuntador a un lugar de memoria, una dirección, una flecha o como ustedes quieran
visualizarla mejor. Todas las variables de los tipos estáticos (todos los que hemos visto hasta el momento) nos
referencian el lugar de la memoria en el que están, por esto son llamadas variables referenciadas y por lo tanto
cambiando el valor de la variable cambiamos el valor del lugar de la memoria, sin embargo con un puntero no es así,
ya que él apunta a un lugar con el cual podemos hacer lo que sabemos hasta el momento, pero el valor del puntero
seguirá siendo el mismo porque la dirección de la memoria sigue siendo la misma, o sea que un puntero no apunta al
lugar de la memoria en que está, sino a otro totalmente distinto. Tal vez leyendo eso ustedes dirán: Pero ya hemos
leído de flechas y lugares de memoria a los que apuntan, y yo les diré que tienen razón, pero la diferencia ahora es
que podemos controlar a esas flechas y además al lugar al que apuntan. Ya comprenderán esto un poco mejor a
medida que avancemos.

Declaración:

Veamos entonces en un ejemplo como definir el tipo puntero hacia enteros:

TYPE
MiPuntero= ^integer;

Como pueden observar, al tratarse de un tipo debe ir declarado debajo de la palabra reservada TYPE, luego le
damos un nombre seguido del signo de igual tal como si fuera un registro, un arreglo u otro tipo definido por nosotros
mismos. Luego escribimos el acento circunflejo (^) seguido inmediatamente del tipo al que apuntaremos sin separar
por espacios. En este caso hemos definido un tipo puntero llamado MiPuntero que apuntará a datos del tipo
integer, o sea, apuntaremos a números enteros. Como en cualquier tipo debemos definir luego una variable de ese
tipo para poder trabajar con él. En este ejemplo:

VAR
puntero1: MiPuntero;

Hasta este punto solo tenemos una variable del tipo puntero, o sea una dirección, pero no está inicializada por
lo cual no apunta a nada, tiene basura y con ella no se puede trabajar. Cualquier intento de trabajo con un puntero no
inicializado terminará probablemente con un error en tiempo de ejecución lo cual no es para nada recomendable, por
lo tanto SIEMPRE INICIALIZEN SUS PUNTEROS, o mejor dicho SIEMPRE INICIALIZEN SUS
VARIABLES. Se que ya dije eso antes, pero nunca está de más recordarlo, aunque pueden verlo en todos y cada uno
de los ejemplos de programas en este manual.
Entonces lo primero que debemos hacer es inicializar nuestro puntero, y para esto tenemos dos maneras: lo
inicializamos de la nada, o sea, pedimos una celda de memoria a la que apuntar para luego poder asignarle valores a
esta y trabajar con ella como con cualquier variable; o bien le asignamos la dirección de otro puntero ya inicializado.
A diferencia de cualquier tipo, a un puntero no podemos asignarle valores, o sea, no podemos dar una dirección
específica de una celda y decirle “apuntá para allí”, excepto si le pasamos la dirección de otro puntero o si le damos el
valor de una constante especial, pero esto lo veremos más adelante. Ahora veamos como inicializar un puntero desde
la nada.
Otro modo de declarar un puntero es, al igual que con arreglos, de forma anónima:
VAR puntero1: ^integer;

El procedimiento NEW y el operador ^:

Bien, un puntero no se inicializa como cualquier variable porque como ya dije no podemos ir y asignar un
valor específico tal y como lo hacemos con los enteros, los caracteres y demás. Simplemente pediremos una celda de
memoria y esta se asignará automáticamente en función de lo que exista disponible. Si se diera el caso en el que no
haya suficiente memoria RAM tendríamos algún problema, pero esto no sucederá para nosotros ya que nuestros
programas no usarán casi nada. Para darle un valor inicial a un puntero tenemos que utilizar el procedimiento NEW,
el cual simplemente llevará entre paréntesis el nombre de nuestra variable puntero. En nuestro ejemplo sería:

NEW(puntero1);

con lo cual ahora tenemos una celda de memoria asignada para nuestro trabajo. Nuestro puntero tiene una dirección
fija, o sea, tiene un valor, pero el lugar de la celda al que apunta contiene basura, tal como sucede con las variables
normales. Si definimos una variable del tipo integer y no la inicializamos esta quedará con un valor que dependerá de
nuestro compilador, lo cual no es para nada seguro. Gráficamente tenemos lo siguiente:

Vladimir Rodríguez 139


Programación estructurada en Pascal

Nuestra variable puntero1 apunta hacia un lugar preciso pero no sabemos qué hay allí. Ahora bien, si un puntero
contiene la dirección de la memoria pero no su valor ¿cómo modificamos lo que existe allí?
En una variable común y corriente como las que conocemos, simplemente al escribir su nombre estamos
referenciando al lugar de la memoria que ocupa y su contenido, con lo cual podemos acceder a él directamente y
modificarlo, mostrarlo en pantalla, asignarle un valor leído de la entrada estándar o hacer cualquier operación. Con un
puntero podemos hacer lo mismo ya que PASCAL nos da la estructura para trabajar con el valor de la memoria como
si fuera una variable referenciada:

puntero1^:= 10;

De este modo estamos asignando el valor 10 a la celda de memoria, tal como si fuera una variable entera
común. Podemos entonces decir que puntero1^ es el identificador de la variable en esa celda. Lo que intento decir
es que del mismo modo que en una declaración del tipo

VAR
X: integer;

utilizamos al identificador X para trabajar con los valores de la memoria, en una declaración del tipo

VAR
X: UnTipoPuntero;

utilizamos al identificador X^ para trabajar con el valor de la memoria. De este modo podemos hace con X^ todo lo
que se puede hacer con las variables comunes, sea READ, READLN, WRITE, WRITELN, así como también
podemos utilizarlos en asignaciones, operaciones, comparaciones y demás.

Entonces, hay algo que tiene que quedar muy claro. Dado un tipo puntero y una variable X del tipo puntero,
una cosa es nombrar a X (el puntero en sí) y otra es nombrar a X^ (el lugar al que apunta X). Diremos que X contiene
la dirección en la que está X^. Si lo vemos gráficamente sería así:

En esta
figura pueden ver el
código PASCAL
completo de lo que
hemos visto hasta
ahora sobre
punteros. Creamos
el tipo
MiPuntero como
punteros a enteros,
luego declaramos la
variable
puntero1 de ese
tipo, pedimos una
celda de memoria
para luego poder
asignarle el valor entero 10 con lo cual termina nuestro programa.

Algo muy pero muy importante es que no pueden hacer referencia a puntero1^ si antes no han
solicitado una celda de memoria ya que si nuestro puntero no apunta a ningún lado ¿qué es entonces puntero1^?
Como dije antes, trabajar con un puntero no inicializado nos dará un error en tiempo de ejecución, y no queremos eso
jamás. Las variables del tipo puntero, a pesar de contener una dirección de memoria, son variables y por tanto su
espacio de memoria se asigna en tiempo de compilación. ¿De que sirven entonces? No es lo mismo mantener estático
el volumen de memoria necesario para almacenar a varios punteros que para almacenar los tipos a los que estos
apuntarán. Veamos un ejemplo para que comprendan esto:
Supongan la siguiente declaración:

TYPE
RegistroPersona= Record
Nombre: String;
Edad: Integer;
Peso: Real;
End;

Persona= ^RegistroPersona; //Un puntero al registro.


VAR
pers1: Persona; //Variable del tipo puntero a un registro.

Vladimir Rodríguez 140


Programación estructurada en Pascal

La variable pers1 es un puntero a un registro, o sea, contendrá la dirección de memoria en la que guardaré
un registro con tres datos, sin embargo pers1 solo guarda una dirección, lo cual es estático para cualquier tipo de
puntero y es infinitamente menor en tamaño que lo que se necesita para almacenar un registro. Por lo tanto es mejor
mantener un volumen de memoria estático para almacenar punteros que para almacenar estructuras de datos
complejas. Si tengo que almacenar tres registros, es mejor ocupar el espacio de tres punteros que soliciten la memoria
cuando haga falta y la liberen luego, que mantener siempre ocupado el espacio para tres registros, los cuales pueden
ser enormes. Imaginen un arreglo de registros tal y como usamos en nuestro programa de productos; dicho arreglo es
una estructura muy compleja y conlleva mucha memoria ¿no sería mejora administrarla de otra manera? Pues sí, y
pronto veremos como hacerlo.

Ahora vean el siguiente código e intenten darse cuenta de qué saldrá en pantalla si ptr1 y ptr2 son
punteros a enteros:

new(ptr1);
new(ptr2);
ptr1^ := 12;
ptr2^ := ptr1^ + 4;
readln(ptr1^); (* suponga que se ingresa 3 *)
writeln(ptr2^ * ptr1^) (* ¿qué despliega? *)

La respuesta correcta es no se sabe, piensen ustedes el por qué.

Alias de variables:

Veamos lo siguiente paso a paso:

TYPE
puntero= ^integer;

VAR
p1, p2: puntero;

Tenemos hasta este momento lo siguiente:

donde ninguna de nuestras variables está inicializada y por lo tanto no podemos hacer nada con ellas. Sigamos
entonces y veamos lo que sucede:

NEW(p1);

Como pueden ver hemos


solicitado un lugar de memoria
para p1, o sea lo inicializamos de
la nada y le damos su espacio para
almacenar un entero; dicho
espacio contiene algún valor
desconocido (basura). La variable p2 sigue sin inicializar por lo que contiene una dirección que no sirve para nada y
que solo nos traerá problemas hasta que le demos un valor.

Ahora hacemos lo siguiente: p2:= p1 con lo cual obtenemos el siguiente resultado:

Hemos asignado a p2 el valor de p1, o sea, le hemos


dicho a p2 que apunte hacia la misma celda que p1. Cuando
dos o más punteros apuntan a una misma celda decimos que
son alias. Esto puede ser muy útil ya pero a la vez es muy
peligroso ya que al modificar la celda de uno modificamos la
del otro, tal como sucede con el pasaje de parámetros por
referencia.

Ahora veamos una asignación similar pero muy


diferente:

Vladimir Rodríguez 141


Programación estructurada en Pascal

Sean p1 y p2 punteros a caracteres (char), dado el siguiente fragmento de código tenemos la siguiente
situación:

New(p1);
New(p2);
p1^:= 'c';
p2^:= 'j';

Noten ahora la diferencia entre estas dos asignaciones suponiendo que las mismas son escritas luego del
código anterior, o sea, o escribimos una o la otra, no una seguida de la otra:

p1^:=p2^ p1:=p2

Como podrán observar, en el primer caso hemos copiado el contenido de la celda de p2 en la de p1 con lo
cual ambas variables (p1^ y p2^) valen lo mismo. Esto es igual a hacer una asignación del tipo X:= Y siendo X e Y
variables de un mismo tipo. Ahora vean el segundo caso donde en vez de usar p1^ y p2^ usamos p1 y p2
directamente. Como podrán observar p1 pasa a apuntar al mismo lugar que p2 transformándose ambos en alias, sin
embargo la celda a la que apuntaba p1 originalmente queda sin nadie que la apunte por lo cual ya no es posible
acceder a ella, pero sigue conteniendo su valor guardado y por tanto sigue ocupada, no ha sido liberada. La forma
correcta de hacer esto habría sido liberar primero la celda de p1 de modo que este quede indefinido y luego hacer la
asignación p1:= p2.
Este tipo de errores es muy común y pronto veremos alguno más. El trabajo con la memoria dinámica
requiere muchísima atención para hacerlo bien y controlar todo correctamente. Por eso muchos programadores
sostienen que es mejor tener un sistema que se encargue de liberar la memoria automáticamente cada cierto tiempo
como lo hace Java y otros no. Yo me inclino por los que no, pero es una simple opinión personal.

Un uso incorrecto de NEW:

Si hacemos NEW de un puntero ya inicializado estaremos pidiendo un nuevo espacio de memoria para él, con
lo cual apuntaremos al nuevo lugar y dejaremos el otro en la nada. Sea p un puntero a caracteres:

NEW(p); p^:= 'a'; Ahora, luego de estas instrucciones hacemos NEW(p);

Como verán, al hacer NEW de un puntero obtenemos un nuevo lugar en la memoria sin importar lo que
hubiera antes. De este modo si el puntero estaba indefinido lo definimos, y si ya estaba definido lo redefinimos sin
importar lo que había antes. De este modo, si ya teníamos un lugar de la memoria asignado, obtenemos uno nuevo a
costa de abandonar el otro pero dejándolo ahí, con o sin valor asignado, pero no liberado y sin posibilidad de volver a
acceder a él.

Tengan esto siempre en mente: NEW nos da un nuevo espacio de memoria y nos apunta hacia él, no importa
lo que haya sucedido hasta el momento, NEW define nuevamente de cero al puntero y ya. Si hacemos treinta NEW
seguidos de un mismo puntero habremos creado treinta lugares de memoria pero solo apuntaremos al último creado y
los otros veintinueve quedarán en nuestra memoria, ocupando lugar, pero sin posibilidad de ser accedidos o usados.
Por suerte los sistemas operativos se encargan de liberar la memoria mal usada.

Vladimir Rodríguez 142


Programación estructurada en Pascal

El procedimiento DISPOSE:

Así como se nos hace necesario solicitar memoria cuando haga falta, se nos hará necesario liberarla cuando
ya no la necesitemos. Hemos visto que, dado un puntero a algún tipo determinado, el procedimiento NEW nos asigna
una porción de memoria y su dirección para que podamos acceder a ella. Por ejemplo, dado un puntero p a cualquier
tipo, si hacemos NEW(p) obtenemos nuestra porción de memoria a la cual llamaremos p^ y su dirección quedará
almacenada en p. Así como es de fácil solicitar memoria lo es el liberarla. Teniendo un puntero ya inicializado,
utilizamos el procedimiento DISPOSE para liberar su porción de memoria, por ejemplo, sea el mismo puntero p a
algún tipo específico y estando p ya inicializado utilizamos:

DISPOSE(p);

De este modo la porción de memoria se libera para que el sistema pueda utilizarla y p queda indefinido, o sea,
queda como si nunca lo hubiéramos inicializado.
Vimos anteriormente que si hacemos sucesivos NEW de un mismo puntero vamos solicitando memoria en
cada uno, pero vamos dejando perdidas las porciones de memoria de los NEW anteriores quedándonos solo con la del
último, lo cual es un desperdicio porque el sistema cree que las porciones no referenciadas están igualmente siendo
utilizadas y eso no es así. Ahora ¿qué pasa si hacemos sucesivos DISPOSE de un mismo puntero? Copien este código
en su compilador y observen el resultado:

PROGRAM SucesivosDispose;

TYPE
TipoPuntero= ^Char;

VAR
puntero: TipoPuntero;

BEGIN
writeln('Presione ENTER para solicitar memoria al sistema');
readln;
new(puntero);
writeln('Hemos solicitado memoria al sistema.');
writeln('Presione ENTER para liberar la memoria solicitada.');
readln;
dispose(puntero);
writeln('Hemos liberado la memoria solicitada.');
writeln('Presione ENTER para liberar nuevamente.');
readln;
dispose(puntero);
writeln('Todo ha salido bien, presione ENTER para salir.');
readln;
END.

Como verán, primero solicitamos memoria inicializando puntero mediante NEW con lo cual obtenemos
nuestro espacio para almacenar un carácter así como la dirección de ese espacio. Luego liberamos ese espacio
mediante DISPOSE con lo cual la información contenida en él se pierde, o sea, deja de existir puntero^ y
puntero queda indefinido, o sea, su dirección es cualquier cosa. Luego nuevamente intentamos liberar memoria,
sin embargo puntero no apunta a nada específico, contiene una dirección errónea porque está indefinido, por lo
tanto no podemos liberar memoria de un lugar al que no podemos llegar lo cual genera un error en tiempo de
ejecución y nuestro programa se cerrará abruptamente (si no es así depende del compilador, sin embargo dejar eso a
suerte es un error en la metodología de programación). Como ven, utilizar DISPOSE con punteros no inicializados es
un error grave y hay que tener mucho cuidado a la hora de utilizar este procedimiento. Por eso es complicado
administrar bien la memoria que utiliza el programa, más aún cuando la cosa se vuelve compleja.

Ahora que saben liberar memoria pueden hacer de forma correcta lo que señalé como error anteriormente, o
sea, cuando tenemos dos punteros inicializados pero queremos transformarlos en alias, o sea, que ambos apunten a lo
mismo, debemos primero liberar la memoria de uno de ellos y luego cambiar su dirección, de este modo no habrá
porciones de memoria que se queden perdidas en el espacio:

Vladimir Rodríguez 143


Programación estructurada en Pascal

Nota: Sea un puntero p hacia cualquier tipo específico, si p no está inicializado no podemos referenciar a p^
porque tal lugar de memoria no existe. Hacerlo causa un error en tiempo de ejecución. De este modo, referenciar al
lugar que apunta un puntero luego de un procedimiento DISPOSE es un error de programación.

Seguiremos con un último concepto para luego ver algunos ejercicios antes de continuar. El siguiente tema
luego de esto será Listas Encadenadas, lo cual está basado en el mero uso de punteros.

UN ERROR COMÚN: Alias y DISPOSE:

Supongan que tenemos dos punteros, p y q, apuntando a una misma celda, o sea, p y q son alias:

Si hacemos DISPOSE(p) ¿qué pasa?

Ahora q quedó también indefinida porque la porción de memoria a la que apuntaba ya no existe. Tengan
mucho cuidado con este tipo de situaciones.

El puntero nulo, la constante NIL:

Hemos visto que una variable de un tipo puntero es una variable que contiene una dirección hacia una porción
de memoria en la que se almacenará un tipo de dato especificado. Vimos también que no es posible asignar valores
específicos a un puntero excepto si le pasamos el valor de otro puntero ya inicializado anteriormente. Existe, sin
embargo, una constante general para cualquier tipo de puntero la cual contiene el valor “nulo” y que podemos
asignarle a cualquiera de nuestros punteros; esta constante es llamada NIL. Sea p un puntero a un tipo cualquiera,
podemos hacer:

Vladimir Rodríguez 144


Programación estructurada en Pascal

p:= NIL;

en cualquier momento. NIL es la forma correcta de decir que un puntero no está apuntando a nada, que es nulo, lo
cual no es lo mismo que decir que no está inicializado. Un puntero que “apunta” a NIL no tiene memoria asignada,
pero tiene un valor correcto, asimismo, como no tiene celda asignada no podemos acceder a ella mediante el operador
^, o sea, para nuestro p, si p=NIL no podemos acceder a p^ porque no existe, si lo hacemos tendremos un error en
tiempo de ejecución, por eso siempre que estemos trabajando en situaciones donde un puntero pueda valer NIL
debemos preguntarlo primero. Esto es posible porque NIL admite comparaciones, o sea, si tenemos un puntero p
podemos hacer por ejemplo:

• IF p=NIL THEN...
o por ejemplo

• IF p<>NIL THEN...
o en cualquier cosa ya que podemos utilizarlo como condición:

• WHILE p<>NIL DO...

• REPEAT ...UNTIL p=NIL


etc.

Asignar el valor NIL a un puntero ya inicializado sin antes hacer DISPOSE de él es un error ya que
cambiaremos la dirección del puntero sin antes liberar la memoria con lo cual quedaría esta perdida en la nada. Es el
mismo caso que asignar a un puntero ya inicializado el valor de otro puntero sin antes liberar sus memoria.

IMPORTANTE: Siempre que vayan a asignar a un puntero ya inicializado algún valor deben liberar la
memoria asignada para él.
Hacer DISPOSE de un puntero NIL es un error de programación, puede no causar problemas, pero no debe
hacerse jamás.

¿Para qué sirve el valor NIL? ¿De qué sirve decir que un puntero no apunta a nada? Pues lo veremos dentro
de poco, primero realicen estos ejercicios simples antes de continuar.

Ejercicio1: a) Determinen cuáles de las siguientes proposiciones son válidas:

• new(apun1)
• new(apun1^)
• apun1 := apun3
• apun2^ := apun2^ + apun1^
• apun1 := NIL
• apun4^ := NIL
• writeln(apun2, apun3)
• read(apun1^, apun4^)

b) Determinen cuáles de las siguientes proposiciones son válidas:

1.apun1 := NIL
2.apun2 := new(apun1)
3.dispose(apun3)
4.apun3^ := NIL
5.apun3 := apun4 AND (apun3 = NIL)
6.apun4^ := NIL

Vladimir Rodríguez 145


Programación estructurada en Pascal

Ejercicio 2:
1.

Consideren las declaraciones que se hicieron en el Ejercicio 1. Determinen la salida que produce el siguiente
código:

new(apun1);
new(apun2);
new(apun3);
apun2 := apun1;
apun1^ := 2;
apun2^ := 3;
apun3^ := ’A’;
writeln(apun1^, apun2^, apun3^)
2.

¿Tiene algún error el siguiente código? Supongan que apun1 es una variable de apuntador que hace referencia
a una variable entera.

new(apun1);
read(apun1^);
writeln(apun1^);
dispose(apun1);
writeln(apun1^)

Ejercicio 3: Consideren las declaraciones realizadas en el Ejercicio 1, determinen la salida del siguiente
código:
new(apun3);
new(apun1);
apun3^ := ’Z’;
apun2 := NIL;
apun4 := NIL;
IF (apun3 <> NIL) AND (apun2 = NIL) THEN
writeln (‘Código A’);
IF apun3^ = ‘Z’ THEN
writeln (‘Código Z’)
ELSE
writeln (‘Código X’)

Ejercicio 4

Dado el siguiente código en Pascal

Type
TipoVehiculo = (barco, camion);
Transporte = record
capacidad : INTEGER;
case vehiculo : TipoVehiculo of
barco : (habitaciones : INTEGER);
camion : ();
end;
var a, b, c : ^Transporte;

begin
new(a);
a^.capacidad := 30;
a^.vehiculo := barco;
a^.habitaciones := 4;

Vladimir Rodríguez 146


Programación estructurada en Pascal

new(b);
b^.capacidad := 4;
b^.vehiculo := camion;

new(c);
c^.capacidad := 5;
c^.vehiculo := camion;
end.

1- ¿Cuál de las siguientes figuras representa el estado de la memoria? (tengan en cuenta que se omiten los
nombres y valores de algunos campos para no recargar el dibujo).

2- Dada la instrucción: b := c

2.1 ¿Cuál de las siguientes figuras representa el estado de la memoria posterior a la instrucción?
(tengan en cuenta que en las figuras se omiten los nombres y valores de algunos campos para no recargar el
dibujo).

2.2 ¿
Sería correcto
hacer dispose(b) antes de la instrucción? Justifiquen.

3- Dada la instrucción b^ := c^

3.1 Dibujen el estado de la memoria posterior a la instrucción.

3.2 ¿Qué valor tiene b^.capacidad?

3.3 Teniendo en cuenta que se quiere mantener las referencias a los componentes referenciados ¿sería
correcto hacer dispose(c ) después de la instrucción? Justifiquen.

Realicen todos estos ejercicios antes de continuar. Pregúntenme acerca de todo aquello que les provoque
dudas, les responderé a la brevedad. Si no logran comprender estos conceptos no podrán trabajar con lo que viene a
continuación. ¡¡¡Adelante!!!

Vladimir Rodríguez 147


Programación estructurada en Pascal

Listas encadenadas simples:


Es aquí donde utilizaremos el verdadero potencial de los punteros. No aprenderemos en sí nada nuevo ya que
en lo que a lenguaje refiere hemos aprendido todo a lo que apunta el manual, esto es simplemente una
implementación de una herramienta ya conocida, una implementación que probablemente no se les ocurriría por sí
mismos y es por esto que en las universidades y cursos de programación se enseña de forma específica.
De forma sencilla, una lista encadenada simple es un listado de varios elementos de un mismo tipo donde
existe uno que es el primero y uno que es el último, tal como en los arreglos. La diferencia es que las listas son
dinámicas y pueden aumentar o disminuir de tamaño llegando incluso a ser vacías, ocupando solo la memoria que
necesitan para existir en el momento preciso. A forma de esquema, tenemos un primer elemento que, además de
contener todos los datos que sean, nos dirá donde está el segundo elemento, que nos dirá donde está el tercero, que
nos dirá donde esta el cuarto, y así sucesivamente hasta llegar a uno que nos diga “Ya no hay más elementos”.

Allí pueden ver un dibujo sencillo de lo que sería una lista encadenada. Cada elemento contiene lo que tiene
que contener y además nos dice donde está el elemento siguiente a él en la lista, hasta que algún elemento nos diga
que ya no existe nada más después de él con lo cual diremos que estamos al final de la lista.
Veamos como aplicar este concepto en PASCAL. Para empezar diremos que cada elemento de una lista es un
nodo, por lo tanto una lista puede ser vacía (cuando no existe nodo alguno), contener un nodo o varios. La gracia de
esto será poder modificar los nodos, quitarlos, agregarlos y moverlos. En este primer ejemplo definiremos lo que es
un nodo de lo que será un alista de números enteros. Pongan mucha atención para comprender esto porque causa
confusión y a veces cuesta entender lo que se está escribiendo:

TYPE
Lista= ^NodoLista;

NodoLista= Record
numero: integer;
siguiente: Lista;
End;

Veamos: Definimos el tipo Lista como punteros hacia el tipo NodoLista, el cual declaramos más abajo
como un registro de dos campos, uno del tipo entero que será nuestra información, y otro con un puntero del tipo
Lista, o sea, un puntero que apuntará a elementos del tipo NodoLista. Al tener un puntero del tipo Lista,
estaremos apuntando a un registro que dentro de él tendrá, además de un número entero, otro puntero del tipo
Lista, y así por cada uno. Cada elemento del tipo Lista podrá apuntar a otro con la misma estructura. Veamos
esto en un dibujo:

Vladimir Rodríguez 148


Programación estructurada en Pascal

Como ven, cada elemento del tipo Lista tiene un puntero que apunta a una estructura del tipo Lista por lo
cual podemos ir repitiendo esa estructura tanto como queramos, donde cada elemento tiene el puntero hacia el
siguiente quedando todos encadenados.
De este modo, para tener una lista simplemente necesito el puntero hacia el primer elemento ya que a partir
de este podré obtener el siguiente y, recorriendo uno a uno, puedo ver la lista completa. Siempre para dar una lista
debe darse el puntero hacia el primer elemento.
Dado este sistema, debemos recorrer siempre las listas de forma secuencial, elemento a elemento, ya que no
podemos ir directamente a un nodo específico. Por ejemplo, en una lista de 10 elementos no puedo ir al elemento
número 5 sin antes recorrer los 4 anteriores. Esto deriva en problemas de eficiencia y metodologías del uso de listas
para solucionarlos, pero no los veremos aquí ya que lo importante ahora es saber trabajar con ellas.

Crearemos, dada la estructura anterior, una lista de tres nodos, guardando el número 10 den cada uno:

1 PROGRAM PrimeraLista;
2
3 TYPE
4 Lista= ^NodoLista;
5
6 NodoLista= Record
7 numero: integer;
8 siguiente: Lista;
9 End;
10
11 VAR
12 MiLista, i: Lista;
13
14 BEGIN
15 New(MiLista); //Creo el primer elemento de la lista.
16 i:= MiLista; //Utilizo un puntero alternativo para iterar.
16 i^.numero:= 10;
17
18 New(i^.siguiente); //Creo el segundo elemento de la lista.
19 i:= i^.siguiente; //Ahora i apunta al segundo elemento de la lista.
20 i^.numero:= 10;
21
22 New(i^.siguiente); //Creo el tercer elemento de la lista.
23 i:= i^.siguiente; //Ahora i apunta al tercer elemento de la lista.
24 i^.numero:= 10;
25 END.

Analicemos este código un poco; tengan en cuenta que lo he hecho así para que vean todo, luego lo haré de la
forma correcta. En la línea 15 invocamos a NEW para que la variable puntero MiLista obtenga un lugar de
memoria para almacenar a un registro NodoLista:

MiLista obtiene la dirección de un registro NodoLista y


apunta hacia allí. Dicho registro tendrá a priori un número entero
cualquiera en su campo numero y un puntero no inicializado en
su campo siguiente.

En la línea 16 utilizo un puntero adicional i para apuntar también al primer elemento de la lista
transformando a MiLista y a i en alias:

En la línea 18 solicito el espacio necesario para alojar a un nuevo elemento. El puntero i^.siguiente es
del tipo Lista, por lo tanto la sentencia New(i^.siguiente) creará un nuevo registro y nos dará su dirección
de memoria:

Vladimir Rodríguez 149


Programación estructurada en Pascal

En la línea 19 hago que i apunte al elemento creado recientemente:

En la línea 20 asigno el valor 10 al nuevo elemento:

En la línea 22 creo el tercer elemento del mismo modo que fue hecho para crear el segundo. Luego hago que i
apunte hacia dicho elemento para asignarle el valor 10 tal como hice anteriormente:

De este modo he logrado construir una lista de tres nodos, cada uno con el valor 10, donde el puntero
MiLista apunta al primero de ellos y todos están encadenados; así, solo obteniendo la dirección de MiLista
puedo recorrer todos los nodos uno por uno.
Veamos ahora un código que hace lo mismo pero mediante un FOR que iterará tres veces. Este código es el
más correcto ya que será igual de largo para crear tres nodos que para crear cien ¿comprenden?:

1 PROGRAM PrimeraLista;
2
3 TYPE
4 Lista= ^NodoLista;
5
6 NodoLista= Record
7 numero: integer;
8 siguiente: Lista;
9 End;
10 VAR
11 MiLista, i: Lista;
12 j: integer; //Un iterador entero para el FOR.
13
14 BEGIN
15 New(MiLista);
16 For j:= 1 to 3 do //Crearé tres nodos.
17 begin
18 If j=1 then
19 i:=MiLista
20 else
21 new(i^.siguiente);
22
23 If j<>1 then
24 i:= i^.siguiente;
25
26 i^.numero:= 10;
27 end;
28 END.

El análisis de este código les queda de tarea para ustedes.

Vladimir Rodríguez 150


Programación estructurada en Pascal

Lista vacía:

Dije hoy por ahí que un lista puede ser vacía o puede contener uno o más elementos, lo cual es totalmente
cierto. Entonces, diremos que una lista es vacía cuando el puntero al primer elemento vale NIL. Por ejemplo, para la
estructura anterior si MiLista= NIL diremos que la lista de número es vacía.

Aunque ya había hablado del valor NIL para un puntero, no había dibujado su
representación gráfica. Como pueden observar allí, no dibujé una flecha, sino un fin
abrupto representado por líneas, eso significa NIL, un puntero que no apunta a nada, lo
cual es muy distinto a un puntero indefinido. Puedo preguntar si un puntero es NIL
mediante una comparación, pero no puedo saber si es indefinido mediante ningún tipo de
sentencia del lenguaje.

Fin de lista:

En la primera lista que hemos visto aquí creamos tres nodos. Ahora haremos un pequeño programa que cree
tantos nodos como el usuario quiera y a cada uno le asigne el número que el usuario quiere. Al terminar nos dirá
cuantos nodos hemos creado. Asumimos en todo momento que el usuario siempre ingresa información correcta:

PROGRAM PrimeraLista;

USES crt;

TYPE
Lista= ^NodoLista;

NodoLista= Record
numero: integer;
siguiente: Lista;
End;
VAR
MiLista, i: Lista;
j: integer; //Un iterador entero para el FOR.
opcion: char; //Para leer opciones de teclado.

BEGIN
//En esta sección de código solo mostramos opciones al usuario, creando
//la lista vacía si el usuario dice que SI.
//
//-----------------------------------------------------------------------
clrscr;
write('¿Desea crear una lista de números enteros? S/N: ');
readln(opcion);
j:= 0;

//Simplemente damos opciones de inicio al usuario.


Case opcion of
'S': begin
MiLista:= NIL;
writeln('Hemos creado una lista vacía.');
writeln;
end;
'N': begin
write('Presione ENTER para salir...');
readln;
exit;
end;
End;//Fin del CASE OF.

{Crearemos tantos nodos como quiera el usuario.


Cada vez que elija la opción 1 crearemos un nuevo nodo y leeremos un
número de la entrada estándar. Terminaremos cuando el usuario lo desee.
De este modo la lista será de un largo totalmente aleatorio en cada
ejecución}
//-----------------------------------------------------------------------

Vladimir Rodríguez 151


Programación estructurada en Pascal

Repeat
//Mostramos las opciones al usuario
clrscr;
writeln('LISTA DE NÚMEROS: ');
writeln;
writeln('1) Agregar nuevo número a la lista');
writeln('2) No agregar más números');
writeln;
write('Opción: ');
readln(opcion);

{Si la opción leída es 1 creo el nodo correspondiente y hago que


i apunte a dicho nodo. Vean que discrimino mediante un IF si la
lista es vacía y si no lo es. Al final leo el número directo sobre
el nodo para luego sumar 1 a mi contador j.}
Case opcion of
'1': begin
clrscr;
write('Número: ');

If MiLista=NIL then
begin
New(MiLista);
i:= MiLista;
end
else
begin
new(i^.siguiente);
i:= i^.siguiente;
end;

readln(i^.numero);
j:= j+1;
end;
'2': writeln('No se ingresarán más números.');
end;
Until opcion='2';
//Terminado el bucle del REPEAT mostramos la información al usuario y ya.

clrscr;
writeln('Se han agregado ',j,' números.');
write('Presione ENTER para salir.');
END.

No he numerado las líneas de este código porque no lo analizare, los comentarios son lo suficientemente
descriptivos como para que ustedes puedan comprender lo que se está haciendo, además de que todo ha sido
explicado con anterioridad. No continúen hasta no comprender este código.

¿Para qué este programa? ¿Qué tiene que ver con el fin de una lista? Bueno, lo que hemos hecho aquí es crear
una lista de largo aleatorio, o sea, crearemos tantos nodos como el usuario quiera en el momento, por lo tanto nunca
es posible saber cuantos elementos tendremos. Entonces, ¿qué pasa si ahora queremos mostrar los elementos de la
lista? Debemos recorrer uno a uno hasta llegar al final de la misma, pero no sabemos hasta donde recorrer. Es cierto
que en este caso hemos contabilizado los nodos creados por lo cual podríamos iterar tantas veces como diga nuestra
variable j, sin embargo esa no es la forma correcta de hacerlo porque en general, los programas que trabajan listas no
van contabilizando lo que crean ya que la complejidad a la que se enfrentan no lo permite y traería muchísimos
problemas, sobretodo al tener que utilizar herramientas como la recursión, que, aunque en este manual no la
aprenderemos, es importante que aprendan a trabajar de forma correcta desde el principio.
No puedo explicitarles aquí la importancia de esto ya que no es a lo que apunta este manual. Normalmente los
programas trabajan con módulos independientes, o sea, partes de código independientes entre sí donde cada una está
en archivos diferentes, y luego existe un archivo principal que se encarga de enlazar (hacer link o linkear) todos los
archivos independientes. De este modo el paso de información entre un módulo y otro se vuelve complejo y cuanto
menos variables y estructuras existan en este pasaje hacen que la programación sea más sencilla, así como el
mantenimiento y la actualización de los programas. En nuestro próximo manual, donde pasaremos al lenguaje
MODULA-2, que es un PASCAL un tanto más avanzado y orientado a la modularización de programas, casi a la
programación orientada a objetos, aprenderán estas cosas y podrán hacer programas mucho más complejos, como
pequeños editores de texto y demás.

Veamos entonces cómo marcar el final de una lista de manera correcta.

Vladimir Rodríguez 152


Programación estructurada en Pascal

Es simple. Cada nodo tiene un puntero que apunta al siguiente elemento de la lista, el último elemento
simplemente apuntará a NIL y ya, con eso diremos que la lista terminó y con eso podremos crear algoritmos muy
eficientes en el tratamiento de listas encadenadas.

Veamos, de forma genérica, ejemplos de códigos para el trabajo habitual con listas encadenadas, o sea,
contabilizar sus elementos, agregar un elemento al principio, agregar un elemento al final, buscar un elemento, borrar
el primero, borrar la lista completa:

Largo de una lista:


function largo(l: lista): integer;
var
contador: integer;
p: lista;
begin
contador:= 0;
p:= l;
while p <> nil do
begin
contador:= contador + 1;
p:= p^.siguiente; (* avanzar a la siguiente celda *)
end;

largo:= contador;
end;
Búsqueda de un elemento:

function pertenece(elem: T; l: lista): boolean;


var
p: lista;

begin
p:= l;
(* notar: evaluación por circuito corto *)
while (p <> nil) and (p^.elemento <> elem) do
p:= p^.siguiente;

pertenece:= (p <> nil);


end;

Como ven en los comentarios, notar la evaluación por circuito corto. Si no lo recuerdan, significa que en una
condición AND, la parte de la izquierda se cumple entonces verificamos la parte de la derecha y en caso de que
ambas sean ciertas el AND es cierto (TRUE). Si la condición de la izquierda es falsa entonces ni siquiera verificamos
la de la derecha. En este caso, si la condición de la derecha es falsa significa que p=NIL por lo tanto, si quisiéramos
verificar la segunda parte tendríamos que acceder a p^, lo cual es un error porque un puntero NIL no apunta a nada.

Agregar elemento al principio:


procedure agregar_al_principio(var l: lista; elem: T);
var p : lista;
begin
new(p); (*crear nueva celda*)
p^.elemento:= elem; (*cargar el elemento*)

Vladimir Rodríguez 153


Programación estructurada en Pascal

(* ajuste de punteros *)
p^.siguiente:= l;
l:= p;
end;

Agregar elemento al final:

procedure agregar_al_final(var l: lista; elem: T);

var p,q : lista;


begin
new(p); (*crear nueva celda*)
p^.elemento:= elem; (*cargar el elemento*)
p^.siguiente:= nil; (*es el último*)

if l = nil then
l:= p
else
begin
(*busco el último de l*)
q:= l;
while q^.siguiente <> nil do
q:= q^.siguiente;

(*engancho p a continuacion del último*)


q^.siguiente:= p;
end;

end;

Borrar el primer elemento de una lista no vacía:

procedure borrar_primero(var l: lista);


var
p: lista;
begin
p:= l;
l:= l^.siguiente;
dispose(p);
end;

Borrar una lista completa:

Para liberar todo el espacio ocupado por una lista es necesario liberar celda por
celda.

procedure borrar_lista(l: lista);


var
p: lista;
begin
while l <> nil do
begin
p:= l;
l:= l^.siguiente;
dispose(p);
end;
end;

Vladimir Rodríguez 154


Programación estructurada en Pascal

Continuemos ahora entonces con los ejercicios acerca de punteros, estos están orientados a las listas:

Ejercicio 5: Dado el siguiente código que permite crear una lista encadenada:

TYPE
apuntador = ^registro;
registro = RECORD
dato : ...; (* integer, char, real, ... *)
sigreg : apuntador;
END;

VAR
apuntaInic : apuntador;
apuntaActual : apuntador;
indice : integer;

BEGIN

new(apuntaInic);
apuntaActual := apuntaInic;

FOR indice := 1 TO 3 DO
BEGIN
new(apuntaActual^.sigreg);
apuntaActual := apuntaActual^.sigreg
END;
apuntaActual^.sigreg := NIL;
END.

¿Cómo modificaría el código anterior si se desea, mediante la inclusión de una tercera variable de tipo
apuntador, apuntar al último registro de la lista?
Suponiendo que el tipo del campo ‘dato’ en el registro definido anteriormente es de tipo char, escriban un
procedimiento en Pascal llamado Buscar que exhiba un mensaje que indique si un valor de tipo char se encuentra o
no en la lista.
El cabezal del procedimiento es el siguiente:

procedure Buscar(c : char; apuntaInic : apuntador)

donde c es el char a buscar y apuntaInic es la referencia del comienzo de la lista.

Ejercicio 6:

1.Definan un tipo llamado ListEnt, el cual representa una lista encadenada de enteros.

2. Escribir un procedimiento en Pascal que permita insertar un entero al principio de la lista encadenada. El cabezal
del procedimiento es el siguiente:
Procedure InsertarPrincipio(valor: integer; VAR lista: ListEnt);

3. Escribir un procedimiento en Pascal que permita eliminar el primer elemento de una lista encadenada. El cabezal
del procedimiento es el siguiente:
procedure EliminarPrincipio (VAR lista: ListEnt);

Ejercicio 7: Dada la siguiente definición:

Type
ListChar = ^registro;
registro = RECORD
campo: char;
siguiente : ListChar
END;

Vladimir Rodríguez 155


Programación estructurada en Pascal

1. Escribir una función en Pascal que permita insertar un carácter al final de una lista encadenada de caracteres.
Ponga especial atención en no modificar la lista con la cual se invoca a la función, sino que se debe copiar toda la
lista, y operar sobre la copia.
El cabezal de la función es el siguiente:
function InsertarUltimo (valor :char; lista: ListChar):ListChar;

2. Escribir un procedimiento en Pascal que permita eliminar los primeros cant caracteres (siendo cant una variable
dada, mayor o igual a cero) de una lista encadenada de caracteres. El cabezal del procedimiento es el siguiente:
procedure DescartarComienzo(cant : integer; VAR lista: ListChar);

3. Escribir una función en Pascal que permita obtener, a partir de una lista de caracteres, otra lista con los caracteres
que se encuentran entre la posición “indBase” e “indSup” de la lista original (siendo “indBase“ e “indSup”
números enteros mayores o iguales a cero). El cabezal de dicha función es el siguiente:
function SubCadena (indBase, indSup : integer; lista : ListChar) : ListChar;

BASE PRODUCTOS – Parte 3

Nuestro programa seguirá haciendo las mismas cosas que en su segunda parte solo que esta vez utilizará la
estructura de listas encadenadas para almacenar los elementos de modo que solo utilizará la memoria necesaria para
la cantidad de productos almacenados en el momento.
Será tarea de ustedes modificar los cabezales de los procedimientos y las funciones que utilizaban la
estructura del arreglo con tope para trabajar con la lista de elementos así como es tarea de ustedes modificar la
declaración de los tipos principales del programa. No necesitan trabajar con listas para corregir errores de la entrada
estandar, solo quiero que lo hagan con la lista de productos.

Si necesitaran que yo les envíe los cabezales de todos los subprogramas no tendré problema en hacerlo ya que
por lo general es lo que se hace, sin embargo será un buen ejercicio para ustedes el tener que pensarlos.

Vladimir Rodríguez 156


Programación estructurada en Pascal

UN ÚLTIMO PRYECTO:

Les propondré aquí el trabajo con lo que fue la segunda tarea de Programación 1 en la Facultad de Ingeniería
donde estudio. Estos proyectos están diseñados para que apliquen todo lo dado aquí y para que piensen de forma
estructurada, la misma forma que he enseñado a lo largo de este manual. Es de alto nivel pero podrán hacerla, yo
estaré abierto a que pregunten todas y cada una de las dudas que tengan al respecto ya que por lo general estos
trabajos se acompañan con alguna clase de consulta. Le daré aquí la letra tal y como fue dada a los estudiantes
incluyendo hasta las reglas de no copiar y demás, mucha suerte. Espero que lancen a realizar esto:

Deben leer todo antes de continuar, ya que luego de la letra del problema existen las instrucciones para
implementarlo ya que se les brindará un archivo con el código ya escrito para la interfaz gráfica y un archivo con el
cual deberán trabajar ustedes creando el motor del juego. Aquí ven un ejemplo de un programa donde un archivo se
encarga de realizar la interfaz gráfica y otro de hacer las tareas del programa, luego ambos trabajan juntos.

1.  Introducción

Este documento presenta el problema que deberá resolverse para la aprobación de la segunda
tarea del laboratorio del curso 2010.

Se presenta información acerca de: normas, recursos, plazos, una presentación general del
problema, las especificaciones del mismo, ejemplos de ejecución y la forma de entrega.

2.  Información general


El estudiante que no respete alguna de las consideraciones que siguen corre el riesgo de que su
trabajo sea invalidado, con la consiguiente pérdida del curso.
Compilador
Todos los programas deben ser compatibles con el compilador del curso (Free Pascal).
No se aceptarán como válidas aquellas tareas que pudieran funcionar con algún otro
compilador Pascal, pero no funcionen con Free Pascal, versión 2.2.2 para windows.
Grupos
Esta tarea se deberá realizar en forma individual.
Para todas las tareas rige el Reglamento del Instituto de Computación ante Instancias
de No Individualidad en los Laboratorios. A continuación se adjunta un fragmento del
mismo; ante cualquier duda se recomienda leer el documento completo
(http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Laboratorio/NoIndividuali
dad)
Los laboratorios deben ser realizados únicamente por los integrantes
del grupo establecido La realización de los laboratorios es
estrictamente individual, sea a nivel unipersonal en el primer caso, o
del grupo establecido en el segundo. Se entiende que compartir total o
parcialmente cualquier actividad del laboratorio atenta contra la
integridad del estudiante universitario y de su formación, y por lo
tanto constituye una falta grave. Específicamente no es posible
compartir por ninguna vía entre integrantes de grupos distintos las
tareas de codificación, digitación, compilación, depuración y
documentación de los programas u objetos (o entregas) del
laboratorio. Además de que no se pueden compartir actividades del
laboratorio, no se pueden compartir los productos de las mismas. Cada
grupo es responsable de su trabajo de laboratorio y de que el mismo
sea individual, independientemente de las causas que pudiesen
originar la no individualidad. A modo de ejemplo y sin ser exhaustivos:
utilización de código realizado en cursos anteriores (por otros
estudiantes) u otros cursos, perder el código, olvidarse del código en
lugares accesibles a otros estudiantes, prestar el código o dejar que el
mismo sea copiado por otros estudiantes, dejar la terminal con el
usuario abierto al retirarse, enviarse código por mail, utilizar código

Vladimir Rodríguez 157


Programación estructurada en Pascal

suministrado por terceros, etc. Asimismo se prohíbe el envío de código


al grupo de noticias del curso, dado que el mismo será considerado
como una forma de compartir código y será sancionado de la manera
más severa posible.
Forma de entrega
Las entregas se realizarán por la web. Para ello se deberá acceder a un sitio destinado
a tal fin y seguir los pasos que se explicarán en la página correspondiente. Esta página
estará disponible cuando comience el plazo de entrega.
Fecha de Entrega
Se debe entregar desde el 4/11 al 10/11. El plazo vence el 10/11 a las 12 de la noche.

3.  Presentación

3.1  Juego Iguales

Iguales es un juego tipo puzle. El objetivo es eliminar fichas obteniendo la mayor cantidad de
puntos posible. Las fichas que están adyacentes las unas de las otras se eliminan como un bloque.
Las demás fichas se colapsan para rellenar los huecos vacíos y se forman nuevos bloques. No se
puede eliminar fichas que no estén en un bloque.

El tablero se inicia como una rejilla llena de fichas. Habrá cuatro tipos de fichas. Si las fichas
adyacentes de un bloque son todas del mismo tipo, entonces pueden ser eliminadas simplemente
seleccionando una de ellas. El número de fichas en el bloque y los puntos que se obtendrán al eliminar
ese bloque se muestran en la parte inferior. Cuanto más fichas tenga un bloque más puntos se
obtendrán. Una vez que el bloque ha sido eliminado, las fichas encima de él comenzarán a caer para
rellenar el espacio. Si se elimina una columna entera, entonces las fichas se desplazarán hacia la
izquierda para rellenar el espacio.

Dos fichas se consideran adyacentes:

• si están en la misma columna y en filas consecutivas, o


• si están en la misma fila y en columnas consecutivas.

Vladimir Rodríguez 158


Programación estructurada en Pascal

En la siguiente figura se muestra paso a paso el resultado de la eliminación de un bloque para el


juego SameGame del entorno Gnome de GNU/Linux.

Se decide eliminar el bloque de fichas verdes.


1. Las fichas se desplazan hacia abajo.
2. Como quedan columnas libres a la izquierda, se desplazan todos las fichas hacia la izquierda.
3. Resultado de eliminar el bloque de fichas verdes.

El juego termina cuando no hay bloques a eliminar.

3.2  Fichas

Cada celda del tablero contendrá una ficha del conjunto {A, B, C, D} o estará vacía. Al comenzar el
juego, el tablero se llena de fichas de forma aleatoria.

3.3  Puntajes

La puntuación está basada en el número de fichas que se eliminen:

Número de fichas eliminadas Puntos obtenidos


2 0
3 1
4 4
… …

Vladimir Rodríguez 159


Programación estructurada en Pascal

n (n - 2)^2

Si se eliminan todas las fichas hay una bonificación de 1.000 puntos.

4.  Arquitectura del sistema

El sistema que implementa el juego se construirá de acuerdo con la siguiente arquitectura:

usuario

+-------+ +----------+

| motor |<-----> | interfaz |

+-------+ +----------+

^ ^

\ /

\ /

v /

estructura

Este es un modelo simple donde tenemos dos capas o módulos: la interfaz y el motor.

La interfaz se encarga de realizar el diálogo con el usuario, capturar sus entradas e invocar las
operaciones asociadas. La interfaz también maneja la imagen del juego en pantalla actualizándola
cada vez que hay cambios. En resumen, se encarga de todo lo que tenga que ver con entrada y
salida. La interfaz no realiza ninguna modificación directa sobre la estructura de datos.

El motor es el módulo que trata con la estructura de datos que representa el juego en un estado
determinado. Este módulo estará compuesto por todos los subprogramas necesarios para ejecutar
las acciones del usuario y reflejar estas en la estructura de datos. El motor no realiza ninguna
operación de entrada y salida.

En esta tarea, el estudiante implementará solamente el motor. La interfaz será provista por los
docentes. En las siguientes secciones explicamos los detalles del módulo a ser implementado.

5.  Estructura

La estructura de datos que representa el juego es la siguiente:

5.1  Estado del Juego

El TipoEstadoJuego es un registro con los siguientes campos:

Vladimir Rodríguez 160


Programación estructurada en Pascal

• tablero: contiene el tablero del juego


• puntaje: contiene el puntaje actual del usuario
• finalizado: indica si el juego ha finalizado.
TipoEstadoJuego = record
tablero : TipoTablero;
puntaje : Integer;
finalizado : Boolean;
end;

5.2  Tablero, Celdas y Fichas

El tablero se representa con la siguiente estructura:

TipoFicha = (A, B, C, D);

TipoEstadoCelda = record
case vacia : boolean of
true : ();
false : (ficha : TipoFicha);
end;

RangoFila = 1..MAX_FILAS;
RangoColumna = 1..MAX_COLUMNAS;

TipoTablero = record
celdas : array [RangoFila, RangoColumna] of TipoEstadoCelda;
topeFila : RangoFila;
topeColumna : RangoColumna;
end;
Esta estructura es una matriz con dos topes (ver array con tope). De esta manera se pueden
representar tableros de diferentes tamaños. Las celdas válidas de la matriz son aquellas cuyas
coordenadas (i,j) son tales que:
• 1<= i <= topeFila
• 1<= j <= topeColumna
Las constantes MAX_FILAS y MAX_COLUMNAS se suponen definidas con valores apropiados. Estos
valores están definidos en la interfaz, de manera que el estudiante no necesita conocerlos.
Cada celda del tablero puede estar vacía, o contener una ficha como indica el registro con
variantesTipoEstadoCelda. Para esta implementación las fichas son 4, y las distinguimos con las
etiquetas: A, B, C o D.

5.3  Posición y Lista de Posiciones

Una posición en el tablero se representa con la siguiente estructura:

TipoPosicion = record
fila : RangoFila;
columna : RangoColumna
end;

Para representar una bloque (o conjunto de fichas) utilizaremos el tipo TipoListaPosicion, que es
un arreglo con tope, como se muestra a continuación:

TipoListaPosicion = record
lista : array [1..MAX_CELDAS] of TipoPosicion;
tope : 0..MAX_CELDAS;
end;

6.  Los subprogramas

Vladimir Rodríguez 161


Programación estructurada en Pascal

El motor del juego está constituido por un conjunto de subprogramas que trabajan sobre la
estructura definida.

6.1  Procedimiento inicializarJuego

Este subprograma es invocado al comienzo del juego para generar el tablero inicial.

procedure inicializarJuego( cuantas_filas :RangoFila; cuantas_columnas :RangoColumna; var


estado :TipoEstadoJuego);

Los parámetros que recibe son:

• cuantas_filas: cantidad de filas que va a tener el tablero


• cuantas_columnas : cantidad de columnas que va a tener el tablero
Este procedimiento debe retornar el parámetro estado configurado para iniciar el juego, esto es:
• para cada celda selecciona al azar una ficha: A, B, C ó D
• el juego no está finalizado
• el puntaje es cero

6.2  Procedimiento obtenerBloque

Dado un tablero y la posición de una celda, devuelve la lista de posiciones de celdas que están en
el mismo bloque según las reglas del juego.

procedure obtenerBloque( tablero :TipoTablero; posicion :TipoPosicion; var bloque


:TipoListaPosicion)

6.3  Procedimiento obtenerBloqueMasGrande

Dado un tablero, devuelve en bloque el bloque más grande. Si hay más de un bloque que cumpla
con esta condición, devuelve cualquiera de estos.
procedure obtenerBloqueMasGrande( tablero :TipoTablero; var bloque :TipoListaPosicion);

6.4  Función puntosAGenerar

Dado un bloque de posiciones a eliminar y un tablero, devuelve la cantidad de puntos que se


generarían si se eliminara el bloque. No solo debe tomar en cuenta la cantidad de posiciones en el
bloque, sino también debe tener en cuenta si el tablero queda vacío o no, pues puede
corresponder sumar el puntaje extra.
function puntosAGenerar( bloque :TipoListaPosicion; tablero: TipoTablero) : Integer;

6.5  Procedimiento ordenarBloque

Este procedimiento ordena una lista de posiciones bloque de forma creciente. Esto es, una celda
[i1, j1] es menor que [i2, j2] si (i1 < i2) o si (i1 = i2) y (j1 < j2). Por ejemplo, la lista ([1,1], [2,1],
[3,4], [3,5], [3,6])está ordenada.
procedure ordenarBloque(var bloque : TipoListaPosicion);

6.6  Procedimiento eliminarBloque

Dado un bloque (o lista de posiciones de celdas a eliminar) y un estado del juego, elimina las
celdas y hace los corrimientos necesarios para que el tablero del juego sea válido nuevamente.
Una vez que el bloque ha sido eliminado, las fichas ubicadas encima de él caen para rellenar el
espacio. Si se elimina una columna entera, entonces las fichas se desplazan hacia la izquierda para
rellenar el espacio. Se debe sumar el puntaje ganado al puntaje total del usuario.

procedure eliminarBloque ( bloque :TipoListaPosicion; var estado :TipoEstadoJuego);

7.  Se pide
Escribir un archivo con todos los subprogramas que forman el motor del juego.

Vladimir Rodríguez 162


Programación estructurada en Pascal

Los cabezales de los subprogramas deben coincidir exactamente con los que aparecen en esta
letra. Si el estudiante realiza algún cambio se considerará que el subprograma no fue
implementado.

Próximamente se publicará la interfaz implementada por el equipo docente, junto con


instrucciones de cómo compilar y ejecutar el programa compuesto por la interfaz y el motor.

Se puede utilizar todo lo visto en las clases teóricas y prácticas.

Para la corrección, las tareas se compilarán con la versión 2.2.2 para windows. La compilación y la
ejecución se realizarán en línea de comandos. El comando de compilación se invocará de la
siguiente manera:

fpc -Co -Cr -Mtp programa.pas


Si trabaja con el IDE, asegúrese de configurarlo para que compile de la misma manera que el
comando anterior (habilitación de Range Checking, Integer Overflow Checking y “Turbo Pascal
Compatible”).
No está permitido utilizar facilidades de Free Pascal que no forman parte del estándar y no se dan
en el curso. Así por ejemplo, no se pueden utilizar ninguna de las palabras
siguientes: uses, crlscr, gotoxy,crt, readkey, longint, string, break, etcétera.

En esta tarea como en todos los problemas de este curso, se valorará además de la lógica correcta,
la utilización de un buen estilo de programación de acuerdo a los criterios impartidos en el curso.
De esta manera, se hará énfasis en buenas prácticas de programación que lleven a un código
legible, bien documentado y mantenible, tales como:

• indentación adecuada
• utilización correcta y apropiada de las estructuras de control
• código claro y legible
• algoritmos razonablemente eficientes
• utilización de comentarios que documenten y complementen el código
• utilización de constantes simbólicas
• nombres mnemotécnicos para variables, constantes, etcétera.

8.  Apéndices

8.1  Números aleatorios

En free pascal se pueden generar números al azar utilizando la función random. Esta función
recibe un parámetro entero positivo y retorna un número aleatorio mayor o igual que 0 y menor
que el parámetro recibido.
El procedimiento randomize debe invocarse una sola vez, previamente a cualquier invocación de la
función random. El único objetivo de esta operación es inicializar el generador de números
aleatorios.

A continuación se muestra un ejemplo donde se generan 10 números al azar entre 1 y 6, como si


fueran lanzamientos de un dado.

{ inicialización }
randomize;
for i:= 1 to 10 do
begin
{ sorteo }
numero:= random(6) + 1;
writeln(numero);
end;
Tener en cuenta que la función random puede repetir números.

Vladimir Rodríguez 163


Programación estructurada en Pascal

8.2  Guía para resolver algunos de los problemas presentados en este obligatorio

Algunos algoritmos tienen complejidades algorítmicas que requieren pensarlos con cuidado. En
este apéndice damos algunas sugerencias para su implementación.

Obtener Bloque

Un algoritmo posible consiste en manejar dos listas de posiciones: lista de pendientes y lista de
visitadas, aparte del bloque (o lista) que queremos obtener como resultado.

El algoritmo es el siguiente:

1. Agregar la posición inicial a la lista de posiciones pendientes.


2. Mientras haya posiciones en la lista de pendientes, procesar cada posición de la siguiente
manera:
1. Quitarla de la lista de pendientes
2. Agregarla a la lista de visitadas
3. Agregarla a la lista de posiciones que forma el bloque que estamos buscando
4. Agregar todas las posiciones adyacentes, que sean del mismo tipo del bloque que
estamos armando, y que no hayan sido visitadas, a la lista de pendientes para
procesarla más tarde.

Eliminar Bloque

Hay varios algoritmos que resuelven este problema. Algunos más eficientes que otros. Una opción
es:

1. Marcar todas las celdas del bloque a eliminar como vacías,


2. Mover todas las fichas hacia abajo para cubrir los huecos que hayan quedado en las
columnas.
3. Si se generaron columnas vacías, mover las columnas que contienen fichas hacia la izquierda
(respetando el orden que tenían), y que las columnas vacías queden a la derecha.

9.  Referencias
1. http://library.gnome.org/users/swell-foop/2.31/swell-foop.html
2. http://en.wikipedia.org/wiki/Same_Game

Instrucciones para la interfaz gráfica:

1.  Introducción
Con el fin de poder probar en forma interactiva los subprogramas que se piden en la tarea 2, se les
brinda el código iguales.pas, al cual le faltan los subprogramas que se pide implementar.
Ud. deberá implementar dichos subprogramas en el archivo motor.pas. Para que esto funcione,
dentro del archivo iguales.pas se encuentra la siguiente directiva: {$INCLUDE motor.pas}. Esto
tiene el mismo efecto que copiar y pegar el contenido del archivo motor.pas dentro
de iguales.pas en el lugar de dicha directiva. Tenga en cuenta que los subprogramas del motor.pas
publicado pueden tener instrucciones a los efectos que compile iguales.pas que no necesariamente
deben estar en la solución implementada.
2.  Restricciones
• El programa final que implemente el juego debe funcionar sin que Ud. tenga que modificar el
archivo iguales.pas. Todos los subprogramas que se pide implementar en la letra de la tarea
2, así como cualquier función, procedimiento, tipo, etc. extra que Ud. necesite definir,
deberán ser implementados en el archivo motor.pas.
• Las directivas, procedimientos y funciones no estándar usadas en el archivo iguales.pasfueron
utilizadas con el fin de que Ud. tenga una interfaz más amigable. Sin embargo, como es regla
en este curso, Ud. no puede utilizar facilidades de Free Pascal que no forman parte del
estándar y que no se dan en el curso para implementar los subprogramas que se piden o
cualquier otro subprograma extra que necesite.

Vladimir Rodríguez 164


Programación estructurada en Pascal

• Ud. no debe usar variable globales, toda la comunicación con la interfaz debe ser a través de
los parámetros de los subprogramas.
3.  Descripción de la interfaz

3.1  Pantalla principal

La interfaz se muestra en la siguiente figura:

Básicamente tiene un cabezal, un tablero y una barra de estado:


• En el cabezal está el nombre del juego y se despliegan mensajes cuando el jugador ha
ganado o perdido.
• En el tablero se ven:
• las celdas vacías (que se representan con el caracter ‘.’) o con fichas (que se
representan con los caracteres ‘A’, ‘B’, ‘C’ o ‘D’ con un color distinto cada uno).
• la posición en donde está parado el usuario (que se indica con el cursor que está
parpadeando).
• el bloque de fichas adyacentes del mismo tipo que la ficha de la posición en
donde está parado el cursor (que se resaltan con un color más brillante)
• En la barra de estado se puede ver:
• la fila y la columna donde está posicionado el usuario,
• la cantidad de fichas del bloque de fichas adyacentes del mismo tipo que la ficha
de la posición en donde está parado el usuario.
• los puntos a generar por el usuario, si borra el bloque de fichas adyacentes del
mismo tipo que la ficha de la posición en donde está parado
• los puntos ya generados en jugadas (o acciones) previas.

Usando las flechas del teclado y algunas teclas se consigue probar y jugar.

3.2  Pantalla inicial

Cuando se inicia el juego aparece la siguiente pantalla, que nos permite elegir el tamaño del
tablero o cargar el estado del juego (nuevo o ya empezado) desde un archivo.

Vladimir Rodríguez 165


Programación estructurada en Pascal

3.3  Ayuda

Por más información Ud. puede ver la ayuda que trae la interfaz. Presionando la tecla “h” (de help
en inglés) se obtiene la siguiente pantalla:

4.  Comunicación interfaz-motor

4.1  Invocación a inicializar Juego(...):


Cuando se inicia un juego nuevo seleccionando el nivel (fácil, intermedio, difícil), la interfaz
invoca al procedimiento inicializarJuego(…) pasando como parámetros cierta cantidad de filas y
columnas dependiendo de cada nivel.

4.2  Invocación a los otros subprogramas del motor

Cuando el usuario se posiciona sobre una celda determinada y presiona algunas de las teclas
correspondientes a eliminar bloque, eliminar bloque más grande, etc., la interfaz invoca los
procedimientos y funciones que correspondan pasando los parámetros que se necesiten, el estado
del juego y/o la posición, dependiendo del procedimiento o función del motor que esté invocando.

Vladimir Rodríguez 166


Programación estructurada en Pascal

Por lo tanto, la comunicación entre la interfaz y el motor se realiza usando solo los parámetros de
los procedimientos y las funciones definidos en el motor.pas.

5.  Funcionalidades

Aparte de poder jugar en forma interactiva, esta interfaz ofrece otras funcionalidades que le
permitirán encontrar y corregir errores más fácilmente, por ejemplo:

• Listar las posiciones del bloque de fichas seleccionado que devuelve el


procedimientoobtenerBloque(…)
• Listar las posiciones del bloque de fichas seleccionado que devuelve el
procedimientoobtenerBloque(…), ordenadas tal cual las ordena el
procedimiento ordenarBloque(…)
• Volver atrás (o deshacer) una acción.
• Guardar la configuración actual del estado del juego en un archivo de texto.
• Cargar la configuración de un estado del juego desde un archivo de texto.
• Guardar en un archivo de texto el historial de los sucesivos estados del juego y las acciones
que se realizaron.
6.  Recomendaciones

6.1  Para compilar

Cree una carpeta (o directorio) donde va a trabajar, descargue los archivos iguales.pas (que
contiene la interfaz) y motor.pas (que solo contiene los encabezados de los subprogramas) y
cópielos a dicha carpeta.
Compile el archivo iguales.pas desde la línea de comandos con: fpc -Co -Cr -Mtp iguales.pas, o
desde el IDE teniendo abierta la ventana con el archivo iguales.pas. El compilador se da cuenta
automáticamente de que tiene que incluir el código del archivo motor.pas donde está la directiva
mencionada antes.

Esto ya va a generar una interfaz sencilla que, si bien no permite crear juegos nuevos ni realizar
acciones, sí permite cargar archivos con juegos de prueba, como se explica a continuación. A
medida que implemente los procedimientos y funciones, podrá realizar más acciones del juego.

6.2  Para probar usando la interfaz

Hasta que usted no implemente el procedimiento inicializarJuego(…), no va a poder generar sus


propios tableros. Pero sí puede ir probando los otros procedimientos y funciones, por ejemplo
cargar el juego desde uno de los archivo de ejemplo que le proporcionamos.

A continuación se proporcionan algunos archivos generados desde la interfaz, los cuales se pueden
cargar sin necesidad de implementar los procedimientos y funciones del motor.

• estadoJuego1.txt
• estadoJuego2.txt
• estadoJuego3.txt
• estadoJuego3-empezado.txt

A continuación se proporcionan algunos archivos con historiales de ejemplo. Los historiales


contienen la secuencia de juegos que se generan a partir de un estado inicial y la aplicación
sucesiva de acciones eliminarBloque o eliminarBloqueMasGrande:

• historial1.txt
• historial2.txt
• historial3.txt

Además Ud. puede probar los procedimientos y funciones implementando su propia interfaz y/o
pequeños programas para probar cada procedimiento por separado. Recuerde que lo que Ud. debe
implementar son los procedimientos, la interfaz es simplemente una ayuda para probar y jugar con
su implementación del motor.pas.

Vladimir Rodríguez 167


Programación estructurada en Pascal

Descarga del programa de prueba: Les dejo aquí el programa ya creado para que puedan probar
como funciona, así ya saben como debe de quedarles. Hagan click aquí para bajar el archivo.

GUÍA PARA REALIZAR TEST DE ESTE PROGRAMA:

1.  Introducción
Como parte de un proceso industrial, la fase de pruebas añade valor al producto generado. Todos
los programas tienen defectos (somos humanos) y la fase de pruebas ayuda a descubrirlos. Ese es
uno de los principales valores que añade.
Probar un programa es ejecutarlo con la peor intención con el fin de encontrarle defectos.

Para ésto se diseñan casos de prueba. Un caso de prueba consta de:

• entradas (parámetros de entrada al programa)


• resultados esperados

En esta hoja encontrarán casos de prueba de ejemplo (casos de prueba básicos) y puntos que
deberían tener en cuenta a la hora de probar.

¡¡ Así que a hincarle los dientes a esto que será de gran ayuda para ustedes!!

2.  ¿Cómo arman un puzzle?

En general, uno empieza por el borde, luego arma las partes más fáciles, las difíciles, y siempre,
para cada parte, uno se va cerciorando que se está uniendo las piezas correctamente. Luego
juntamos las partes prearmadas hasta llegar a la figura final.

3.  ¿Por qué estamos viendo cómo armar un puzzle?

La idea de cómo armar un puzzle e implementar y probar el juego iguales tiene muchas
similitudes. El borde y algunas partes ya están prearmados y testeados (iguales.pas) y le pedimos
que armen y testeen las partes faltantes (motor.pas) para completar el juego o “puzzle”. Parece
una tarea fácil, ¿no?

Por lo tanto para probar su tarea deben tener en cuenta 3 puntos de prueba y en este orden:

1. Probar cada una de las partes armadas por ustedes por separado. O sea, cada subprograma o
procedimiento por separado.
2. Probar que las partes encajan correctamente en lo que ya está armado. O sea, que al unir los
subprogramas implementados por ustedes con los implementados por nosotros no generen
errores.
3. Probar finalmente que todas las partes junto con el borde funcionan correctamente. O sea,
nuestro puzzle es el que está en la imagen de la caja. Esto se hace probando desde la
interfaz. Acá es dónde se pone más divertido, es hora de jugar.

Como bien dice la letra de la tarea “Ud. puede probar los procedimientos implementando su
propia interfaz, y/o pequeños programas para probar cada procedimiento por separado. Recuerde
que lo que Ud. debe realizar son los procedimientos, la interfaz es simplemente una ayuda para
probar y jugar con su motor.”

4.  Indicaciones y pautas a seguir para las pruebas

4.1  Probar por separado los subprogramas

Prueben todos los procedimientos por separado. Esta técnica les será de gran ayuda al momento
de hacer la prueba desde la interfaz. Es decir, disminuirá significativamente la cantidad de errores
que encontrarán y su gravedad. Pueden armarse un programa principal auxiliar que simplemente

Vladimir Rodríguez 168


Programación estructurada en Pascal

haga la llamada al procedimiento bajo prueba y luego imprima el resultado en pantalla para que
puedan verificar la correctitud de su resultado.

A modo de ejemplo se presentan los siguientes programas:

• PruebaInicializarJuego.pas
• PruebaObtenerBloque.pas

que sirven para probar en forma independiente los procedimientos incializarJuego y


obtenerBloque. Recomendamos que armen programas similares para probar el resto de los
subprogramas por separado.

4.2  Pautas para los casos de prueba

Recomendamos que piensen los casos de pruebas antes de empezar a probar y que anoten sus
ideas. Recuerden que un caso de prueba se puede decir que está formado por entradas (en este
caso, parámetros del procedimiento), un resultado esperado y un resultado obtenido. Además se
puede agregar una descripción para entender rápidamente qué se está atacando.

La idea es que prueben lo que se les sugiere abajo. Si tienen algunas ideas más, no las descarten,
úsenlas para probar, incluso en combinación con estas.

inicializarJuego

• Diferentes dimensiones de tableros, chicos, medianos, grandes


• Casos límites en las dimensiones: máximo tablero (MAX_FILAS * MAX_COLUMNAS), mínimo
tablero (1 x 1)
• Verificar que el juego no está finalizado
• Verificar que el puntaje es cero
• Verificar que las fichas se distribuyen aleatoriamente sobre el tablero

obtenerBloque

• Diferentes ubicaciones de la posición: en el centro del tablero, en el borde derecho, en el


borde izquierdo, en el borde superior, en el borde inferior
• Diferentes fichas en la celda de la posición: con ficha A, B, C, D, vacía
• Diferentes adyacentes a la celda de la posición: con adyacentes a la derecha, a la izquierda,
arriba, y abajo
• Sin adyacentes

obtenerBloqueMasGrande

• Diferentes estados, situaciones, posiciones (como en el caso anterior)


• Probar con un tablero con solo bloques unitarios
• Probar con un tablero lleno de fichas del mismo color (el bloque es todo el tablero)
• Probar con un tablero con bloques del mismo tamaño

puntosAGenerar

Siendo n la cantidad de fichas en el bloque:

• Probar cuando el resultado es 0 puntos (n=0, n=1, o n=2)


• Probar para n>2
• Probar para un n grande

ordenarBloque

• Ordenar un bloque de tamaño>1


• Ordenar un bloque de tamaño 1
• Ordenar un bloque de tamaño cero
• Ordenar un bloque que abarque todo el tablero

eliminarBloque

• Eliminar un bloque del centro del tablero

Vladimir Rodríguez 169


Programación estructurada en Pascal

• Eliminar un bloque que provoque uno o varios desplazamientos de columnas.


• Eliminar un bloque de tamaño 1 (esto no debería tener efecto)
• Eliminar un bloque que abarque todo el tablero

4.3  Integrar el motor.pas con iguales.pas

Además de manejar bien las estructuras ya definidas, deben estar seguros que los datos que les
llegan son los esperados por ustedes y los datos que devuelven son los esperados por la interfaz. En
otras palabras deben verificar que están tomando correctamente los datos que les pasan por
parámetro y devolviéndolos correctamente también.

4.4  En el juego, desde la interfaz

Esta es la última. Si hicieron bien las pruebas anteriores no deberían tener muchos errores en esta
etapa. Les sugerimos que para esta parte lo que más deben hacer es jugar. Recuerden que la
intención debe ser encontrar fallas y no salteárselas. Lo mejor es enfocarse en buscar cierto tipo
de errores durante varias partidas seguidas. Piensen que tienen una misión, encontrar defectos,
entonces por varias partidas ponen el foco en buscar ciertos defectos, por ejemplo, eliminar
bloques y verificar que las demás fichas se desplazan correctamente. Es importante que se
guarden los historiales de cada partida(*), así cuando ocurre una falla, pueden reproducirla. Esto
les servirá al momento de verificar que hayan arreglado el defecto. Les podrá ser útil anotar en
una planilla la correspondencia entre los errores y los archivos de historial. En las misiones,
pueden intercalar los distintos niveles de tablero de la interfaz, o alguno armado por ustedes.

(*) Recuerde que la interfaz proporcionada por los docentes, le permite guardar y cargar desde un
archivo de texto el estado de un juego (recién iniciado, o ya comenzado). También se puede
guardar en un archivo de texto el historial de los estados por los que pasó el juego y las acciones
que se realizaron.

5.  Indicaciones útiles

Cuando utilicen casos de prueba

• Ejecuten todos los casos de prueba y anoten sus resultados antes de comenzar la revisión de
los que fallaron. Esto les facilitará el trabajo.
• A la hora de empezar la revisión de los casos que fallaron. Se puede seguir estos pasos:
• Revisar la formulación del caso de prueba. Puede que esté mal el caso y no sea el
programa.
• Si el caso de prueba es correcto, se pasa a detectar qué parte del código está
generando la falla. Esto lo pueden hacer debugueando por ejemplo.
• Cuando hayan arreglado todos los errores, ejecuten los casos de prueba que en un principio
no fallaron. Esto es para asegurarse que los cambios hechos no introdujeron errores en lo que
andaba bien.

Algunas indicaciones más para cuando prueben desde la interfaz:

• Al igual que con los casos de prueba, ejecuten varias misiones, las que ustedes crean
convenientes, antes de empezar a corregir los errores.
6.  Conceptos de testing manejados en esta guía
• Testing Unitario (probar cada parte por separado)
• Testing de Integración (probar cómo encajan las partes entre sí)
• Testing de Sistema o Caja Negra (probar que el puzzle armado es igual al de la imagen en la
caja)

Vladimir Rodríguez 170


Programación estructurada en Pascal

Despedida y Agradecimientos:

Hasta aquí hemos llegado con este manual, de verdad espero que les haya servido para algo así como esperaré
sus correos para preguntar, corregir, sugerir y demás. La creación de este documento está inspirada en el
agradecimiento a todos aquellos que al igual que yo han decidido utilizar su tiempo para ayudar a quienes buscan
información en la web, una ayuda sin fines de lucro sino más bien todo lo contrario, una ayuda sin la cual yo hoy no
sabría nada. Muchas gracias a todos ellos, muchas gracias a quienes publiquen y difundan este manual, a todas
aquellas páginas web que permitan su descarga gratuita, a todos quienes se tomen enserio este curso y aprendan
realmente.

No es una despedida para siempre porque continuaré con más manuales, manuales para los cuales asumiré
que ya han leído este. En la segunda entrega, como ya dije, abordaremos el lenguaje MODULA-2 el cual sigue siendo
PASCAL pero orientado a la modularización. Con dicho lenguaje aprenderemos a crear programas separados por
módulos individuales, lo cual nos da infinitas posibilidades; abordaremos la recursión como herramienta y sin la cual
no podrán solucionar muchos problemas, además de que les proveerá del poder de realizar taréas que de otro modo
serían imposibles; abordaremos estructuras como las de árboles, colas y otras tantas usadas en muchos tipos de
programas; aprenderán acerca del diseño de TAD (Tipos Abstractos de Datos) entre muchas otras cosas. Los
programas seguirán siendo vistosamente horribles pero potencialmente competentes, lo cual es lo más importante,
porque de nada sirve saber crear hermosas interfaces gráficas si nuestros programas no saben trabajar.

Estoy pensando también crear un anexo a este manual, no para aprender conceptos nuevos, sino para traducir
estos conceptos a un lenguaje moderno como Java, a modo de incentivo. El aprendizaje seguirá orientado a la
segunda entrega de este tutorial, por lo cual lo otro es un simple agregado a este, pero aún lo estoy pensando ya que
Java obliga a aprender conceptos de la programación orientada a objetos y aún no hemos trabajado con ello en
absoluto.

Habiendo leído todo este manual cualquiera de ustedes debería poder iniciarse en otro lenguaje si leen
manuales de él, podrán al menos hacer casi las mismas cosas que hasta ahora pero tal vez con un modo de trabajo
mucho mejor y un tanto más sencillo. Sin embargo esperaré que lean la próxima entrega, la cual espero esté lista para
mediados de este año, o sea, Junio o Julio de 2011.

Gracias nuevamente a todos los que me han leído, a todos los que me escribirán y a todos quienes decidan
aportar algo más a esto. Estaré a su disposición siempre que necesiten ayuda y también para recibirla ya que yo
también estoy aprendiendo, recién ingresando a este mundo tan complejo y tan interesante. Gracias a todos y a todas.

Un cordial saludo de su amigo Vladimir Rodríguez. Ha sido un gusto escribir esto y, aunque me ha llevado su
tiempo, hoy me siento feliz de poder mostrarlo. Gracias.

Vladimir Rodríguez 171


Programación estructurada en Pascal

Vladimir Rodríguez 172


Programación estructurada en Pascal

ÍNDICE DE CONTENIDO

PRIMERA PARTE
Conceptos básicos de programación
Introducción 4
Nociones sobre Lenguaje de Programación 5
Instalando el ambiente de desarrollo Free-Pascal 6
Verificando Instalación 8
Corrigiendo Bug y configurando para comenzar 9
Configuración Range Checking e Integer Overflow 10
NUESTRO PRIMER PROGRAMA: ¡¡¡HOLA MUNDO!!! 11
Write y Writeln 13
Introducción a las variables 14
Ejemplo de asignación 15
Leer más de una variable a la vez 15
Ejercicio 16
Introducción a la compatibilidad de tipos 17
Errores en tiempo de Ejecución y errores en tiempo de Compilación 18
Palabras reservadas 19
Identificadores 20
Tipos de datos 20
Operadores en pascal 20
Precedencia de operadores 21
Funciones matemáticas de pascal 21
Un ejemplo del uso de DIV y MOD. Introducción a los comentarios 22
Introducción a las constantes 23
Booleanos 26
Algunas notas 26
Ejercicios 26

SEGUNDA PARTE
Selección y Repetición
Introducción 28
Selección. Instrucción IF...THEN e IF...THEN...ELSE 29
Anidación de la instrucción If...then 30
Anidación de un if...then con un if...then...else 30
Condiciones compuestas 32
Tablas de verdad 32
Operadores relacionales 33
Un ejemplo matemático 33
Instrucción de selección CASE...ELSE 36
Ejercicio 38
Secuencia de repetición FOR 39
Anidación de FOR 40
Ejercicios 41
DEBUGER 43
Secuencia de repetición WHILE...DO 44
Un pequeño juego 46
Generar números aleatorios 47
Secuencia de repetición REPEAT...UNTIL 48
Centinela 48
Ejercicio 49
Un ejemplo más complejo. Contador de palabras 49
Ejemplo de captura de error 49
Tabla Ascii 50
Ejercicios 51

Vladimir Rodríguez 173


Programación estructurada en Pascal

TERCERA PARTE
Introducción a los TIPOS definidos por el programador
Subrangos y Arreglos
Introducción 54
Subrangos 55
Arreglos 56
Declaración 56
Utilización de arreglos 57
Hacer WRITE y READ de un arreglo 59
Ejercicios 59
Declaración anónima de una arreglo 60
Recorriendo arreglos 60
Búsqueda de un elemento 62
Ejercicios 63
Arreglos multidimensionales 65
Arreglos bidimensionales 65
Ejercicios 66
Arreglos tridimensionales 67
REALIZANDO UN PROYECTO: MASTER MIND 68

CUARTA PARTE
SUBPROGRAMAS: Procedimientos y Funciones
Introducción 72
Procedimientos 73
Declaración y llamado 73
Variables Globales y Locales. Introducción al Alcance de Identificadores 76
Sombreado de Identificadores 79
Parámetros: Pasaje por Valor 80
Declaración de parámetros de tipos diferentes 82
Asignaciones de parámetros 83
Parámetros Nominales y parámetros Efectivos 83
Funciones 84
Declaración y llamado 84
Funciones Booleanas 85
Llamar a un subprograma desde otro subprograma 87
Parámetros: Pasaje por Referencia 88
Ejercicios 93

QUINTA PARTE
Enumerados
Registros con y sin variante
Arreglo con tope
Introducción 100
Enumerados 101
Read y Write con enumerados 102
Comparaciones con enumerados 102
Ejercicios 105
Registros 107
Declaración 107
Una estructura compleja 109
Ejercicios 109
BASE DE PRODUCTOS-Parte 1 110
Limpiar la pantalla 117
La función WITH...DO 118
Ambigüedad con WITH...DO 119
Registro con variante 120
Arreglo con tope 123
Ejercicios 124
BASE DE PRODUCTOS-Parte 2 130

Vladimir Rodríguez 174


Programación estructurada en Pascal

SEXTA PARTE
Memoria Dinámica: El tipo Puntero
Listas Encadenadas Simples
Introducción 138
El tipo Puntero 139
Declaración 139
El procedimiento NEW y el operador ^ 139
El procedimiento DISPOSE 142
El puntero nulo, la constante NIL 144
Ejercicios 145
Listas encadenadas simples 148
Lista vacía 151
Fin de lista 151
Largo de una lista 153
Búsqueda de un elemento 153
Agregar un elemento al principio 153
Agregar elemento al final 154
Borrar el primer elemento de una lista no vacía 154
Borrar una lista completa 154
Ejercicios 155
BASE DE PRODUCTOS-Parte 3 156

UN ÚLTIMO PROYECTO
Letra 157
Instrucciones para la interfaz gráfica 164
Descarga del programa de prueba 168
Guía para realizar el test de este programa 168

DESPEDIDA Y AGRADECIMIENTOS 171

Vladimir Rodríguez 175


Programación estructurada en Pascal

Vladimir Rodríguez 176

Das könnte Ihnen auch gefallen