Curso de Inicio A La Programacion (PASCAL)
Curso de Inicio A La Programacion (PASCAL)
Curso de Inicio A La Programacion (PASCAL)
Cada vez que termine uno de los temas deberá realizar los ejercicios asociados que
se encuentran en el apartado Actividades - Unidades del Centro Virtual de
Educación.
Necesitan de alguien que les indique lo que tienen que hacer. Y ese "alguien" es el
programador. Pero una vez que saben qué hacer y cómo, lo pueden repetir siempre
que deseen, sin error posible y a velocidades muy superiores a las que podría
hacerlo una persona.
Programa
Con el paso del tiempo los modelos de programación, más conocidos como
"paradigmas de programación", han ido evolucionando y han surgido nuevos
enfoques. Inicialmente la programación en código máquina y la programación en
lenguaje ensamblador estaban fundamentadas en instrucciones que hacían
operaciones y en instrucciones que alteraban la ejecución secuencial de las mismas
a través de saltos incondicionales. La propuesta de Wirth a través del lenguaje
Pascal asentó dos paradigmas:
En general todos los lenguajes de alto nivel tienen propiedades comunes: el uso de
sentencias simples, la existencia de variables, expresiones, estructuras de control y
subprogramas. Todos estos conceptos y su utilización serán objeto de los temas
siguientes.
El lenguaje Pascal es uno de los más adecuados para aprender y consolidar dichos
conceptos.
Clasificación
Clasificar nunca es fácil puesto que requiere establecer a priori unos criteros
concretos que permitan dividir en clases o tipos el conjunto de elementos a
clasificar. Dado que es muy frecuente que estos elementos tengan aspectos
comunes, otros con ciertos matices y otros claramente diferentes, la clasificación
siempre puede no ser tan rigurosa como cabe pensar. Vamos a realizar varias
clasificaciones de los lenguajes de programación en función de diferentes criterios:
Las características más importantes que deben presentar este tipo de lenguajes son
las siguientes:
Entornos de programación
Una vez editado nuestro programa es necesario que este sea procesado y
tranformado en ódenes que puedan ser ejecutadas por el ordenador. Estas órdenes
por tanto deben estar en el único lenguaje que la máquina entiende: el código
máquina. Para ello son necesarios los procesadores de lenguaje cuyo concepto es
muy amplio. Dentro de los procesadores de lenguaje destacan los traductores, los
compiladores y los intérpretes.
Hay otros lenguajes de programación que combinan ambas estrategias como por
ejemplo sucede con el lenguaje de programación Java . Para este lenguaje existen
traductores que generan un programa en un código denominado intermedio que
luego será ejecutado a través de un intérpre que recibe en este caso el nombre de
máquina virtual Java.
Enlazadores
Depuradores
Diseño
Una vez que el análisis ha permitido especificar lo que "tenemos que hacer" y los
requisitos que deben cumplirse es momento de determinar cómo lo vamos a
realizar. Hay que determinar el método que vamos a seguir para resolver el
problema, qué partes o bloques va a tener el programa, qué lenguaje de
programación se va a utilizar, etc.
Se deben tener en cuenta las siguientes premisas:
Codificación
Prueba
Para que se haga una idea de lo importante de esta fase, en las empresas de cierto
tamaño las personas que desarrollan el programa son distintas de las que lo
prueban. Y en aplicaciones con riesgo para la vida humana (hospitales, centrales
nucleares, aeropuertos, etc.) la fase de prueba puede suponer un 80% del tiempo
total de desarrollo.
Aprender a depurar el propio código es por tanto uno de los aspectos más
relevantes a la hora de aprender a programa.
Mantenimiento
Desarrollo en cascada
Aquí debes decir qué variables vas a utilizar, para qué te va a servir cada una y qué
valor inicial, si es necesario, les vas a dar
RECOGIDA DE DATOS
Se especifica qué datos tiene que introducir el usuario del programa y en qué
variables los vamos a meter
PRESENTACIÓN DE RESULTADOS
Algoritmo:
Podemos observar que sólo se pasará a calcular el valor medio cuando el número
leído no sea positivo.
Características de un algoritmo
Leer datos
Realizar cálculos
Escribir resultados
Bien, parece sencillo. Tenemos claramente diferenciadas tres partes del programa.
¡Divide y vencerás!
Ahora se hace necesario ir detallando un poco más las partes anteriores. Esto es lo
que llamamos refinamiento sucesivo. Una posible solución sería la siguiente:
Diagramas de Flujo
Pseudocódigo
Diagramas de Flujo
Utiliza símbolos (cajas de distinta geometría) unidos por flechas (líneas de flujo)
que indican el orden o dirección del flujo del programa.
Programación Modular
En este paradigma:
Por otra parte, si hay partes o cálculos de nuestro programa que se repiten, se
codificarán como módulo una única vez y se llamará varias veces a ese módulo
desde donde haga falta. Los módulos reciben datos de entrada denominados
parámetros que son procesados internamente para devolver un conjunto de
resultados. Obviamente tanto los parámetros como los resultados no son siempre
imprescindibles.
Secuencial
Selectiva
Repetitiva
Introducción
Estructura de un programa en Pascal
Objetos de un programa
Nuestro primer programa
Introducción
Pero fue a partir de los años 80 con la aparición de los compiladores de Borland
(Turbo Pascal y posteriormente Borland Pascal) cuando el lenguaje se hizo popular
en todos los ámbitos. Estos compiladores unían calidad y muy buen precio e
incorporaban herramientas y utilidades que facilitaban la creación de programas. El
fabricante Borland fue sacando al mercado versiones de dicho compilador y
finalmente con el auge del desarrollo de aplicaciones para los entornos gráficos
puso en el mercado una herramienta llamada Delphy basada en Pascal. Hoy en día
los desarrollos de Turbo Pascal se han quedado obsoletos aunque las características
y elementos introducidos son considerados un estándard "de facto", por ello es
habitual que los compiladores de Pascal señalen la compatibilidad con los
compiladores de Borland. Actualmente existen diversos compiladores de Pascal que
permite realizar todas las actividades de este curso. Se incluye un compilador
gratuito de fácil instalación y uso para plataformas Windows y se dan referencias a
otras alternativas para que el alumno tenga libertad a la hora de elegir su entorno
de trabajo. El cambio a otro compilador, entorno o sistema operativo no debe
suponer ninguna dificultad.
Por otro lado la Web tiene numerosas referencias al lenguaje, a su historia, a las
características y por supuesto a las múltiples herramientas de desarrollo que se han
ido produciendo. A modo de ejemplo la Wikipedia tanto en español como en inglés
tienen información y enlaces de
interés. http://es.wikipedia.org/wiki/Pascal_(lenguaje_de_programaci%C3%B3n) h
ttp://en.wikipedia.org/wiki/Pascal_(programming_language)
Estructura de un programa en Pascal
program
uses
Esta palabra reservada va seguida de los nombres de las unidades (units) que
utiliza nuestro programa. Pero, ¿qué son las unidades? Son lo que en otros
entornos se conocen como bibliotecas. Son un conjunto de utilidades de las que un
programador puede disponer para construir su programa. Algunas las proporciona
el entorno de programación y otras se las puede construir usted mismo o las puede
proporcionar un tercero. Esta sección no es imprescindible si no se necesitan
bibliotecas o módulos extra, como verá en el desarrollo de este curso no necesitará
utilizarla.
procedure y function
begin - end
Este es el cuerpo del módulo principal del programa. Como se había indicado
anteriormente este módulo es el que determina el flujo del programa. La
instrucción begin indica el lugar donde comienza a ejecutarse el programa, y la
palabra reservada end marca el punto final del mismo.
Palabras reservadas.
Identificadores.
Comentarios.
Separadores.
Símbolos especiales.
Palabras reservadas
Identificadores
1. Debe comenzar por una letra (a..z o A..Z) y no puede contener blancos.
2. A partir del primer carácter están permitidos dígitos y el carácter de
subrayado (_).
3. No se pueden utilizar palabras reservadas como identificadores
saldo_medio
¿Y cómo distingue el compilador los comentarios del programa? Para que un texto
se considere comentario debe ir encerrado entre (* y *):
{ Esto es un comentario }
Símbolos especiales
Son símbolos formados por uno o más caracteres con un significado específico.
Hemos visto algunos: el punto y coma como separador de sentencias o el punto
final que da lugar al fin de nuestro programa. También forman parte de estos los
operadores matemáticos. El listado completo es el siguiente:
Separadores
Para que las palabras reservadas, identificadores o símbolos sean distinguibles unos
de otros hacen falta separadores. Es Pascal se usan como separadores el espacio
en blanco, el tabulador o el carácter de nueva línea (Intro). Además los
separadores permiten dar claridad a la lectura del código
Mi primer programa
Program Bienvenido;
Const
HOLA='Bienvenido al mundo de la programacion';
Var
nombre: string[40];
begin
write('Introduzca su nombre: ');
readln(nombre);
writeln(HOLA,' ', nombre);
readln;
end.
Es importante guardar el fichero con la extensión PAS en el disco duro. Una vez
editado y guardado el programa es necesario compilarlo antes de poder ejecutarlo.
Si utiliza el entorno integrado para compilar seleccione la opción del menú Execute
- Compile y compruebe si hay notificación de errores en la ventana inferior. Si el
error que aparece es Resource file - Icon file not found (please change it in Project
Options se debe a que la versión de Dev Pascal ha creado el directorio con los
iconos con el nombre equivocado. Consulte este documento de instalación y uso del
compilador, apartado 9, para resolver este problema.
Tipos Simples
Variables y constantes
Expresiones
Sentencias básicas
Tipos de Datos
Si nuestro programa gestiona las nóminas de la empresa, datos serán los nombres
de los empleados, sus sueldos, direcciones, estado civil, NIF, etc.
Según su complejidad:
1. Tipos de Datos Simples (o básicos).
2. Tipos de Datos Estructurados (conjunto o agrupación de datos simples
relacionados de alguna forma).
Según su gestión:
1. Estáticos: aquellos en los que el tamaño está fijado antes de ejecutarse
el programa. El espacio de memoria reservado para ellos no varía a lo
largo de la ejecución del programa.
2. Dinámicos: aquellos en los que el tamaño puede cambiar durante la
ejecución del programa. El espacio de memoria reservado para ellos
puede variar a lo largo de la ejecución del programa.
Numéricos
Lógicos
Carácter
Datos numéricos
En Pascal el tipo entero se corresponde con el tipo integer. Este tipo tiene un
rango finito, lo que significa que no se puede almacenar cualquier número entero
ya que la capacidad está fijada de forma previa. En concreto, los enteros que se
pueden representar incluyen todos los valores desde -32768 hasta 32767, y tal
como se ha señalado no pueden tener decimales.
El tipo real en Pascal se corresponde con el tipo de mismo nombre: real. Se puede
representar este tipo en:
Notación científica.
La codificación en notación científica o coma flotante consiste en
escribir el número utilizando primero una mantisa (M) multiplicada por
la base 10 (D) elevada a un exponente (exp). D esta forma el número N
cumpla:
N = M x 10exp.
Y esto se representa como M E exp. Ejemplos:
2.345E22 es 2.345 x 1022
9.0E-2 es 9.0 x 10-2
Datos lógicos
Este tipo de dato sirve para almacenar información del tipo VERDADERO o FALSO.
Por ejemplo: ¿está la luz encendida? Sólo tenemos dos posibilidades: SI o NO. O,
¿es usted mayor de edad?. ¿Tiene usted carnet de conducir?. Como vemos, es un
tipo de dato muy frecuente en la vida real.
Veremos próximamente las operaciones posibles sobre este tipo de datos, que
generalmente ocupan muy poco espacio en la memoria del equipo.
Existe un tipo muy relacionado con el tipo de datos carácter que se denomina tipo
cadena. Una cadena (también llamada string) es una secuencia de caracteres
delimitados por comillas simple (entre comillas simples). Ejemplo: 'Esto es una
cadena', 'Error', '237'. Por longitud de la cadena se entiende el número de
caracteres que componen la misma.
En Pascal el tipo cadena o string se representa mediante la palabra
reservada string. En el tema de tablas hablaremos nuevamente de este tipo.
Constantes y Variables
Decimos que son constantes aquellos datos que no sufren cambios a lo largo de la
ejecución de un programa. Las variables, por contra, son datos que sí están
sujetos a cambios durante la ejecución.
Constantes
De tipo numérico
Pueden ser enteras (7777, 43, -34) ó reales (4.321, -32.45)
De tipo lógico
Pudiendo estar fijadas al valor VERDADERO o al valor FALSO (true/false).
De tipo carácter
Por ejemplo: 'B', 'o', 'O', '0', '+'.
Dentro de estas se pueden considerar las de tipo cadena:'esto sería una
constante de cadena'
Const
iva = 18;
irpf = 15;
euro = 166.386;
error = 'Algo ha salido mal...';
Var
velocidad: real;
resultado: boolean;
letra: char;
NumHijos: integer;
Const
HOLA='Bienvenido al mundo de la programacion';
Var
nombre: string[40];
begin
write('Introduzca su nombre: ');
readln(nombre);
writeln(HOLA,' ', nombre);
end.
Variables locales y globales
Según el lugar del programa donde se declaran las variables pueden ser:
A lo largo del curso irá viendo que se recomienda no utilizar variables globales. Esto
es porque así se asigna a un determinado módulo del programa la responsabilidad
sobre la gestión de dicha variable, manteniendo el programa más seguro.
Expresiones
Bien, ya sabemos que son las variables y las constantes. Pero poco podemos hacer
con ellas solas. Necesitamos usarlas y operar con ellas para obtener resultados. Por
ejemplo, si está haciendo la factura de una venta, necesita calcular el IVA del
importe de la misma. Y para ello necesitamos lo que se conocen como operadores.
Los operadores, junto con las variables y constantes, formarán lo que se conoce
como expresiones.
1. Constantes
2. Variables
3. Símbolos de operaciones más conocidos como operadores
4. Paréntesis
5. Llamadas a funciones (módulos que devuelven un valor como resultado)
Expresiones Aritméticas
Son aquellas en las que el resultado y los operandos son de tipo numérico. A
muchos de estos operadores ya estamos acostumbrados: suma, resta, producto,
división, etc.
+, -, *, /
Suma, resta, multiplicación y división. Aplicadas sobre enteros dan como
resultado un entero, y sobre reales un real. Si alguno de los operandos es
real el resultado es real.
div
División entera: calcula el cociente entero.
9 div 2 daría como resultado 4.
mod
Calcula el resto de la división entera.
9 mod 2 daría como resultado 1.
Expresiones Lógicas
Dentro de los operadores LÓGICOS hay que considerar también los operadores
relacionales que actúan sobre operandos de tipo NUMÉRICO y de tipo CARÁCTER, y
producen resultados de tipo LÓGICO. Son los siguientes:
Por lo tanto
'c' > 'm' da como resultado FALSO
1. Asignación
2. Lectura
3. Escritura
Asignación
variable := expresión
1. Operación de Lectura:
o Se toma un valor (o varios) de un periférico y se asignan a una
variable (o a varias variables) en el orden de aparición:
o LEER nombre_var1, nombre_var2
La función write (escribir) presenta por pantalla lo que le indiquemos entre sus
paréntesis. De hecho, en la lección anterior se usó en nuestro primer programa:
write('Bienvenido a este curso de Programación');
Este sería su uso más sencillo. Pero es más interesante presentar por pantalla el
valor almacenado en variables:
que imprime la cadena Su nombre es: seguida del valor almacenado en la variable
nombre, que en el caso de que nombre contenga el valor 'Marisa' mostraría por
pantalla el resultado:
Este sería su uso más simple. Se pueden leer varios valores, y las
variables donde se almacenen deben estar separadas por el carácter coma
(,):
read(nombre, edad);
Con la función read, estos se quedan "pendientes", esperando ser leídos por
la próxima sentencia read (si la hay).
En cambio, si se le proporcionan a readln más valores de los que espera
descarta todos los que le sobran (los últimos).
Veamos este comportamiento con un ejemplo en el que aparecen dos elementos
nuevos cuya misión es evitar que la ventana de ejecución del programa se cierre
una vez finalizado el programa impidiendo que se visualice el resultado.Estos dos
elementos nuevos son la declaración de la unidad crt en el epígrafe uses y
elprocedimiento readkey que espera a que el usuario pulse una tecla:
Se asignará
edad = 44
peso = 68
y se queda pendiente de leer el valor 72 que se asigna a altura a
continuación:
altura = 72
En cambio con el uso de readln y los mismo datos de entrada:
Si
Si compilamos y ejecutamos según se presenta en la siguiente figura:
se asignará
edad = 44
peso = 68
se descartara el valor 72, y se le asigna a continuación el valor 1.75 a la
altura:
altura = 1.75
Podemos observar que la sentencia write que presenta el valor de las variables
tiene un formato novedoso:
writeln('Edad: ', edad, ' Peso: ', peso, ' Altura: ', altura:4:2);
El formato en el que se presentan los tipos numéricos puede ser modificado
especificando el número de caracteres que se usarán para imprimirlo (4 en este
caso) y el número de estos que serán decimales (2).
Ya conocemos como usar constantes, como asignar valores a variables y como leer
o escribir mediante las funciones de entrada y salida. En el tema siguiente
estudiaremos como podemos modificar la ejecución de nuestro programa mediante
las estructuras de control.
Este capítulo se puede seguir de forma secuencial o bien accediendo directamente al apartado que se
desee:
Selectivas
Bucles: DESDE
Bucles: MIENTRAS
Bucles: HASTA
Sentencias Selectivas
Estas estructuras, también llamadas bifurcativas, condicionales o alternativas, dividen o ramifican el flujo
del programa según una determinada condición. Pero, ¿qué es una condición?
Una condición es una expresión que da como resultado un valor del tipo lógico o booleano
(verdadero/falso, true/false, 1/0). Este concepto se introdujo al estudiar los tipos de datos.
¿Por qué puede ser interesante bifurcar el flujo un programa? Para que el programa se comporte de forma
distinta en función de la evaluación de la condición. Por ejemplo, si una persona es mayor de edad o no
(edad>=18), el rango en el que está su nivel de ingresos ((ingresos>=1000) and (ingresos<2000))…
Simples
Dobles
Múltiples
De las estructuras selectivas, la más sencilla es la selectiva simple. Se evalúa una condición; si es
verdadera (true) se ejecuta una sentencia (o grupo de sentencias, que podríamos denominar sentencia
compuesta). Si es falsa, se "salta" dicha sentencia y se sigue ejecutando el programa.
La expresión de la condición puede ser compleja, pero debe dar como resultado un valor booleano: true o
false. La representación gráfica de esta estructura, para cualquier lenguaje de programación, es la que se
puede observar en esta figura:
La representación gráfica de esta estructura, para cualquier lenguaje de programación, es la que se puede
observar en esta figura:
un cierto interés:
if (saldo > 0) then
saldo := saldo * 1.02;
Si la sentencia es compuesta deberá comenzar y terminar dicho conjunto de sentencias con las etiquetas
begin-end, con su correspondiente ;.
Continuando con el ejemplo anterior, muy posiblemente el banco también comprueba si estamos en
números rojos, aplicando el correspondiente interés:
if (saldo < 0) then
begin
saldo := saldo * 2.20;
writeln('Saldo Negativo');
end;
Selectiva Doble
Insistiendo con nuestra relación con el banco, le mostramos un ejemplo de la estructura selectiva doble:
if (saldo < 0) then
begin
saldo := saldo * 1.20;
writeln('Saldo Negativo');
end
else (* (saldo > 0) *)
begin
saldo := saldo * 2.02;
writeln('Saldo Positivo);
end
Le presentamos como segundo ejemplo de esta estructura de control una sección de código del programa
Euros. Este programa traslada cantidades de pesetas a euros o viceversa. La parte principal de este
programa es la siguiente:
if (opcion='P') then
begin (* Ejemplo de uso de sentencia compuesta *)
write(cantidad:10:3); (* 10:3, formate el resultado limitando la
anchura a 10 caracteres y tres decimales.*)
write(' euros son ');
resultado:=cantidad*EURO;
write(resultado:10:3);
write('pesetas.')
end (*No se terminan con ;*)
else
if(option='E')then
begin
resultado:=cantidad/EURO;
writeln(cantidad:10:3,'pesetas son', resultado:10:3, 'euros.');
end;
writeIn('Opcion no permitida');
Debe tener precaución con el terminador de sentencia (;) y el brazo else: Nunca ponga un ; antes de un
else puesto que el compilador pensaría que la sentencia if ha terminado y mostraría un error al compilar,
pues no reconoce la sentencia else. Esta precaución esta comentada en el ejemplo previo.
Selectiva Múltiple
Dentro del conjunto de las selectivas se incluye la que algunos autores llaman selectiva múltiple. Esta
permite seleccionar la ejecución de una sentencia entre más de dos alternativas. A diferencia de las
selectivas anteriores, en vez de una condición se evalúa una expresión que da como resultado un valor de
tipo ordinal, un entero o carácter. Una vez obtenido este valor, que llamaremos selector, se compara con
los distintos valores de las distintas alternativas. Cada una de estas alternativas tiene asociada una
sentencia, simple o compuesta. En el momento que coincida el valor del selector, se ejecuta la sentencia
asociada a esa alternativa y termina la ejecución de la selectiva múltiple, continuando la ejecución del
programa al final de esta. Si terminamos de comparar todas las alternativas y no coincide con ninguna, se
ejecuta lo que se conoce como acción o sentencia por defecto.
Esta sentencia se puede omitir. Este comportamiento se refleja con el siguiente diagrama de flujo:
En Pascal la estructura selectiva múltiple se corresponde con la sentencia case-of. Las distintas
alternativas se especifican mediante etiquetas. Una etiqueta es una lista de constantes separadas por
comas. Las etiquetas están separadas de las sentencias que le corresponden mediante el carácter ':'. La
sentencia por defecto se marca con la palabra reservada else.
case (selector) of
etiquetas_1: sentencia_1;
etiquetas_2: sentencia_2;
.
.
etiquetas_n: sentencia_n;
else
sentencia_por_defecto
end (* se corresponde con el case *)
Una sección de código del programa Calc. Este programa realiza las operaciones básicas de una
calculadora: sumar, restar, multiplicar y dividir. Observe las palabras reservadas case-of-else-end.
case (opcion) of
'S','s' : resultado:= primero+segundo;
'R','r' : resultado:= primero-segundo;
'M','m' : resultado:= primero*segundo;
'D','d' : resultado:= primero/segundo;
else
writeln ('Opción no permitida');
end;
Selectivas anidadas
Cuando alguna de las sentencias que aparece dentro de los brazos o alternativas de una sentencia selectiva
es otra sentencia selectiva, se dice que están anidadas. No hay límite al número de sentencias que se
pueden anidar, siempre que estén totalmente incluidas unas en otras, como en el ejemplo del programa
Euros que se había presentado anteriormente. Si existen estructuras if anidadas, las palabras reservadas
else se corresponden con el if inmediatamente anterior, el más cercano. En el siguiente ejemplo, sección
de código del programa Banca, se vuelve a observar la importancia de sangrar (tabular) correctamente el
código:
actualizar := false;
saldo := 7500;
if (actualizar) then
Estas estructuras, también llamadas iterativas, provocan que un conjunto de sentencias del programa se
repita un determinado número de veces. El número de estas repeticiones puede estar prefijado de
antemano, o depender de la evaluación de una condición. A estas estructuras se las conoce como bucles.
Cada vez que se ejecuta 1 vez el conjunto de instrucciones del bucle se dice que se ha producido una
iteración.
Imagine que quiere aparcar el coche en la puerta de casa, y no hay sitios libres: daremos vueltas a la
manzana hasta que haya un hueco libre. Este es un ejemplo de estructura repetitiva: repetir una acción
(dar una vuelta a la manzana) hasta que se cumpla una determinada condición (que haya un hueco libre).
O tareas tan simples como descargar el coche después de la compra: Repetimos un conjunto de acciones
(sacar bolsa del coche, llevarla a la cocina, repartir la compra en las estanterías) mientras se cumpla una
condición (que queden bolsas en el coche).
Incluso si hace deporte, y corre por las mañanas en su parque preferido, posiblemente utilice una
estructura repetitiva: dar una vuelta al parque (acción que se repite) hasta que pasan 30 minutos, o
estemos muy cansados, o empiece a llover (condición del bucle). En este caso la condición de salida del
bucle es más compleja: es una condición lógica compuesta, conectada con el operador lógico O (OR en
inglés). Recuerde que con este operador la condición será verdadera (true) si cualquiera de las
condiciones es cierta. Si tiene dudas, repase el tema tipos de datos y operadores.
Tipos de sentencias repetitivas
Según como se controle la ejecución del bucle encontramos tres tipos de sentencias repetitivas:
Desde
Mientras
Hasta
El bucle DESDE
Este bucle se utiliza cuando de antemano conocemos el número de veces que se tiene que repetir el
conjunto de sentencias. Imagine que en el ejemplo de correr por el parque por las mañanas, la condición
fuera dar un número fijo de vueltas al parque , llueva o estemos cansados. La condición de finalización
del bucle será que el número de vueltas realizadas sea mayor o igual que el prefijado.
una variable de control o variable índice que controla el número de veces que se repite un bucle
(número de iteraciones).
fijar un valor inicial para la variable de control.
fijar el valor límite que tomará la variable de control.
El funcionamiento del bucle es el siguiente:
Lo que está totalmente desaconsejado es modificar la variable de control dentro del bucle. Si necesita
hacerlo, posiblemente el bucle DESDE no es el adecuado.
for v_control := valor_inicial to final do sentencia_a; O para el caso que queramos ir de mayor a menor,
de "cuenta atrás": for v_control := valor_inicial downto final do sentencia_a; Como ejemplo inicial,
observe un bucle que imprime por pantalla el mensaje 'Bienvenido a este curso' diez veces.
for i:= 1 to 10 do
O si desea un conjunto de acciones, utilizamos el par begin-end. Observe en ambos ejemplos la variable
de control, i; valor inicial, 1; valor final, 9.
for i:= 1 to 9 do
begin
end;
Lo que lo hace realmente potente al bucle DESDE es la posibilidad de usar el valor de la variable índice
dentro de las sentencias del bucle.
begin
end.
En cada iteración del bucle, la variable de control indice toma distintos valores, empezando en el valor
que le demos a la variable primero y terminando en el valor de la variable segundo.
En cada una de las iteraciones vamos acumulando en la variable suma el valor previo más el valor que
toma la variable de control en dicha iteración. En la primera iteración, suma será cero y la variable de
control toma el valor de primero.
Pero no tiene porqué creerse la tabla anterior. Quizás sea más interesante que lo compruebe usted mismo.
Para ello vamos a incorporar a nuestro programa lo que se conoce como trazas de depuración, o de
comprobación. Son sentencias de salida write que muestran por pantalla por donde está fluyendo el
código de nuestro programa.
En el caso de la suma, observe que hemos incorporado ahora dos sentencias writeln antes y después del
bucle, y dentro del bucle antes y después de la suma. Estas sentencias nos muestran el valor de primero,
segundo, indice y suma.
suma := 0; (* Hay que iniciar a cero antes de usar por primera vez su valor en
suma:=suma+indice*)
dep := true; (* ACTIVA/DESACTIVA true/false las trazas de depuración *)
if (dep) then
writeln('** Antes del bucle: primero = ', primero, ' segundo = ', segundo, ' suma = ', suma);
for indice:= primero to segundo do
begin
if (dep) then
writeln('** Iteracion ', indice, ' ANTES de sumar. Suma = ', suma);
suma := suma + indice;
if (dep) then
writeln('** Iteracion ', indice, ' DESPUES de sumar. Suma = ', suma);
end;
if (dep) then
writeln('** Despues del bucle: primero = ', primero, ' segundo = ', segundo, ' suma = ', suma);
writeln('La suma desde ', primero, ' hasta ', segundo, ' es ', suma);
Tras la ejecución de este nuevo código, el resultado será similar a:
Estas trazas son muy útiles a la hora de localizar errores en nuestros programas. Normalmente, una vez
comprobado el funcionamiento de estas trazas se eliminan del código. En otras ocasiones, se incluyen
estas trazas en una selectiva simple, controlada por una variable de tipo lógico que el programador inicia
a true o false.
if (dep) then
writeln('** Antes del bucle: primero = ', primero, ' segundo = ', segundo, ' suma = ', suma);
Estudie el código final de la suma en SumarDep.pas. Ejecútelo y pruebe a sumar entre distintas parejas de
valores.
A partir de ahora podrá (y deberá) usar estas trazas en sus ejercicios y ejemplos para comprobar el
correcto funcionamiento de sus programas.
Bucles DESDE anidados
No hay límite al número de bucles que se pueden anidar, siempre que estén totalmente incluidos unos en
otros.
Se pueden anidar dos bucles desde, y cada iteración del bucle externo provoca una iteración completa del
bucle interno.
Veamos como ejemplo de bucles anidados este programa que presenta por pantalla las tablas de
multiplicar.
for i:= 1 to 10 do
for j:=1 to 10 do
No olvide antes de continuar estudiar el código de los ejemplos previos: Suma y Multiplica.
1. Se evalúa la condición.
2. Si la condición es falsa, no se ejecuta el bucle.
3. Si la condición es verdadera, se ejecuta el bucle y se vuelve al punto 1.
En esta figura:
Puede ser que si la condición es falsa en su primera evaluación, las sentencias que forman el bucle
no se ejecute ninguna vez.
Debe existir dentro del bucle algún mecanismo para modificar la condición que controla el bucle y
evitar un bucle infinito, donde la iteración se repite indefinidamente porque no es posible
modificar la condición de salida del bucle. En este caso el programa no acabaría nunca.
while (condicion) do
sentencia_a;
No olvidemos que la sentencia que se repite puede ser una sentencia compuesta, con su correspondientes
begin-end
while (condicion) do
begin
sentencia_a_1;
sentencia_a_2;
sentencia_a_3;
end;
Como ejemplo del uso de este bucle, se calcula el factorial de un número entero positivo. El factorial de
un número entero positivo es el resultado de multiplicarlo por todos los que le preceden hasta llegar a 1.
A esta operador se le asigna el símbolo de exclamación.
Por lo tanto:
el factorial de 2 ( 2! ) es 2*1
el factorial de 3 ( 3! ) es 3*2*1
el factorial de 4 ( 4! ) es 4*3*2*1,
Observe el fragmento de código, y la tabla que le acompaña donde se muestran los valores de las distintas
variables en las iteraciones del bucle. A la variable factorial, que acumula el producto, se le da un valor
inicial de 1.
factorial := 1;
cantidad := 7;
while (cantidad > 0) do
begin
factorial := factorial * cantidad;
cantidad := cantidad -1;
end;
Repase el código de este ejemplo en Factorial.
Program Factor;
(*
Uso de bucle mientras
*)
Var
cantidad: integer; (* Factorial que
deseamos calcular *)
factorial: integer; (* Resultdo *)
begin
writeln('Calculo del factorial de un numero entero ');
write('Introduzca el numero: ');
readln(cantidad);
Sólo nos queda una estructura repetitiva por estudiar: el bucle "repetir-hasta".
Bucle Repetir-Hasta
En esta figura:
Se ha representado con un pequeño círculo el lugar donde se reunen las flechas de flujo del
programa. En ese lugar no hay sentencias de ningún tipo.
En esta estructura, cuando se cumple la condición se acaba el bucle, al contrario que en el bucle
mientras.
Si la condición no se cumple, regresamos a ese círculo previo al conjunto de sentencias del bucle.
repeat
sentencia_a;
until (condicion);
En este caso, si la sentencia es compuesta, no son necesarios los identificadores begin-end, ya que los
identificadores repeat y until hacen de delimitadores de bloque.
Este tipo de bucles es muy adecuado para situaciones en las que a priori conozcamos que hay que ejecutar
el bucle al menos una vez. Este es el caso de programas que solicitan al usuario opciones por pantalla, que
posteriormente hay que comprobar que son correctas.
Vea como ejemplo otra versión de la calculadora básica ya estudiada:
repeat
end;
writeln(' El resultado es ', res);
write(' Terminar (S/N)?: ');
readln(opcion);
Observe el bucle repeat-until y dentro de este la selectiva múltiple. Ejecute el código para ver su
comportamiento completo.
Es posible que en otros lenguajes en vez de la estructura repetir-hasta tenga disponible la estructura
repetir-mientras. La única diferencia que la ejecución se mantiene hasta que la condición sea verdadera o
hasta que se haga falsa.
Vistas las estructuras de control básicas y una vez que haya repasado los ejemplos de este tema, pase al
siguiente tema: subprogramas o módulos.
Este capítulo se puede seguir de forma secuencial o bien accediendo directamente al apartado que se
desee:
Introducción
Paso de parámetros
Funciones y procedimientos
Ejemplos
Introducción
Se ha establecido en temas anteriores que la programación modular es necesaria para realizar programas
correctos. Para facilitar esta tarea los lenguajes de programación proporcionan lo que se conoce como
módulos, subrutinas o subprogramas.
Dividir un determinado problema en partes, agrupando cada una de estas partes en un módulo.
Programar tareas que se repiten varias veces a lo largo del programa, de forma que se codifican
una única vez.
Parametrizar una determinada tarea: permitir que un subprograma sea capáz de realizar una misma
tarea con distintos datos de entrada.
Todo programa en Pascal (y en general en cualquier lenguaje de programación de alto nivel) tendrá un
módulo llamado principal.
Este módulo hace las veces de director de orquesta: el programa empieza en la primera sentencia de este
módulo y termina con la última sentencia de este. Si el problema a resolver es muy simple todo el
programa se codifica en dicho módulo. Si es más complejo, serán necesarios otros módulos, y el módulo
principal será el encargado de llamarlos.
En Pascal el módulo principal es el cuerpo begin-end. del programa. Si existen otros módulos, estos se
definen antes del cuerpo del programa principal, como vimos al estudiar la estructura de un programa en
Pascal.
Conexiones entre subprogramas. Transferencia de parámetros
Si se descompone un programa en partes o módulos será necesario que se comuniquen de alguna forma
para que los resultados de un módulo sean utilizados por otro. Es necesario que los subprogramas se
transmitan información.
Suponga que desea codificar un módulo que calcule el precio de venta al público (PVP) de un producto:
esté deberá recibir de alguna forma el precio de ese producto y el IVA que se le aplica. Si el módulo se
llama calcula_PVP, se espera un funcionamiento similar a:
Módulo calcula_PVP: muestre por pantalla el precio final de un libro de valor 1000 pesetas, al que se le
aplica un 7% de IVA.
Se le proporciona información al módulo para que realice su tarea: el valor del libro y el IVA que se le
aplica. Se dice que el valor del libro y el IVA son parámetros del subprograma. Esto significa que este
módulo o subprograma se puede utilizar para productos de distinto precio y con distinto valor de IVA.
Al mismo tiempo el módulo me devuelve información: el precio final del libro. Esta información se puede
devolver de distintas formas:
Como parámetro.
Como resultado del módulo.
Por algún periférico de salida, por ejemplo la pantalla. De esta forma el programa no puede seguir
usando el resultado para otros cálculos.
Utilicemos el ejemplo del PVP de un producto para estudiar como se definen y utilizan los módulos en
Pascal:
Una función se define (y veremos que un procedimiento se define de forma similar) siguiendo un
esquema similar al de un programa. Estudiemos la función PVP y desglosemos sus partes:
Function PVP(valorprod: real; ivaprod: real): real;
var
total_iva : real;
begin
total_iva := valorprod*ivaprod/100;
PVP := valorprod + total_iva;
end;
En la primera línea se define el nombre de la función (PVP), los parámetros (valor e iva y sus
tipos) y finalmente el tipo de datos del valor que devuelve la función (real).
A continuación las secciones de constantes (const), tipos (type) y variables (var). En este ejemplo
sólo aparece la sección de variables.
Estas variables definidas dentro de una función son las que denominábamos locales.
Finalmente, el cuerpo de la función, encerrado entre un par begin-end;. La sentencia end final
está terminada en ";" y no en un "." como en el cuerpo del programa principal.
La última línea de la función es una sentencia de asignación que le da valor a una variable, que
debe tener el mismo nombre que la función. De esta forma la función devuelve un valor como
resultado de su ejecución. Esta línea debe ser siempre la última.
Llamada o invocación de una función
Para que dicha función se ejecute simplemente hay que "invocarla" o llamarla desde otro módulo,
escribiendo su nombre y colocando entre paréntesis los valores que queremos que reciba la función para
trabajar. Como la función devuelve un valor, es necesario que se le asigne dicho valor devuelto a una
variable, o que se utilice dentro de una expresión:
total := PVP(1700, 10);
Recuerde que en una sentencia de asignación primero se ejecuta la parte derecha del operador ":= ". En
este caso estamos invocando o llamando a la función PVP, y proporcionándole como información que el
valor del producto es 1700 pesetas y el IVA es del 10 %, y esperamos que nos devuelva como resultado el
PVP. Una vez que se ha ejecutado la función PVP, nos devuelve el valor 1870, que se lo asignamos a la
variable total.
En esta llamada hemos usado constantes (1700 y 10), pero se pueden usar en la invocación a la función
variables, a las que previamente se les ha asignado un valor:
valor_libro := 1500;
iva_libro := 7;
total := PVP(valor_libro ,iva_libro);
o la invocación puede ser parte de expresiones más complejas:
total := numero_articulos * PVP(valor_libro ,iva_libro);
Hemos introducido el concepto de parámetro. Debemos distinguir entre los parámetros que aparecen en la
definición de la función y los parámetros que aparecen en la llamada a la función:
Los parámetros reales son los que se utilizan (ya sean expresiones o variables) cuando se invoca a
un módulo. En los ejemplos previos, 1500 y 7, o valor_libro e iva_libro son parámetros reales.
Los parámetros formales son los que se usan en la definición de un módulo. En el ejemplo del
PVP, parámetros formales son valor e iva. Dentro de la función, los parámetros formales se tratan
como variables locales de la función.
Correspondencia de parámetros
Podemos observar que la función tiene parámetros formales únicos, pero que cada vez que llamamos o
invocamos a la función los parámetros reales pueden ser distintos.
Cuando se invoca un módulo, se establece una correspondencia entre los parámetros reales y los
parámetros formales.
Esta correspondencia se va a efectuar según el orden en que aparezcan en la sentencia de llamada,
y según el orden en que fueron declaradas (correspondencia posicional).
Por lo tanto, el número de parámetros reales ha de coincidir con los parámetros formales, y
además han de ser del mismo tipo.
Finalmente, esta correspondencia podrá ser de dos tipos, por valor o por referencia. Estudiaremos
estos tipos en apartados posteriores.
jemplo Transferencia de parámetros
Hemos visto qué son los parámetros, cómo se definen las funciones, qué son parámetros formales y
reales, y como se corresponden los parámetros al hacer la llamada a la función.
Pero antes de continuar con este tema, vamos a intentar entender, mediante trazas, como se produce la
transferencia de parámetros. Para ello vamos a situar trazas en nuestro programa PVP antes de la llamada
a la función, y dentro de la propia función.
total_iva : real;
begin
if (dep) then
begin
end;
total_iva := valorprod*ivaprod/100;
PVP := valorprod + total_iva;
end;
begin
total := 0;
dep := true; (* ACTIVA/DESACTIVA las trazas de depuración *)
write('Introduzca el valor del producto: ');
readln(valor);
write('Introduzca el IVA del producto: ');
readln(iva);
writeln;
if (dep) then
if (dep) then
total := PVP(valor,iva);
if (dep) then
writeln('** Despues funcion PVP. Valor: ',
valor:0:1, ' IVA: ', iva:0:1, ' total: ', total:0:1);
writeln;
writeln('El precio final es: ', total:0:1);
writeln;
(* *********************** *)
if (dep) then
total := PVP(3000,20);
writeln;
writeln('El precio final es: ', total:0:1);
writeln;
(* *********************** *)
if (dep) then
if (dep) then
total := PVP(valor*10,20-2);
writeln;
writeln('El precio final es: ', total:0:1);
end.
Las trazas de depuración están destacadas en color rojo: antes de las llamadas a la función PVP, y dentro
de esta función.
En especial, observe la traza dentro de la función. Está justo al comienzo de dicha función. Cuando se
ejecute dicha sentencia no se ha ejecutado ninguna línea de código de la función.
total_iva := valorprod*ivaprod/100;
PVP := valorprod + total_iva;
end;
Observe que dicha traza intenta presentar por pantalla los valores almacenados en los parámetros
formales valorprod e ivaprod. El código completo lo puede encontrar en preciodep.pas.
Observe las tres llamadas a la función PVP que hay en el programa principal:
total := PVP(valor,iva);
Los parámetros reales son dos variables, valor e iva. El primero, valor, se corresponde con el
parámetro formal valorprod, y el segundo, iva, con ivaprod.
Dentro de la función, estos parámetros toman los valores de los parámetros reales sin que exista
ninguna sentencia de asignación. No es magia: se ha realizado de forma automática la
correspondencia de parámetros entre reales y formales.
total := PVP(3000,20);
En esta los parámetros reales son constantes. Observe en el gráfico resultado de la ejecución la
correspondencia.
total := PVP(valor*10,20-2);
En esta los parámetros reales son expresiones: la variable valor*10, e iva-2. Observe en la figura
que valores toman los parámetros formales dentro de la función: 1000 y 18.
Tipos de correspondencia de los parámetros
por valor: cuando se pasa un parámetro por valor, en el momento de la llamada a la función se
copia el valor del parámetro real en el parámetro formal. Si modificamos el parámetro formal
dentro de la función NO se modifica el parámetro real:
En Pascal, por defecto, los parámetros se pasan por valor. En el ejemplo previo, los dos
parámetros formales son por valor. Hasta este momento todos los parámetros definidos son por
valor.
por referencia: Cuando se pasa un parámetro por referencia, se considera que el parámetro real y
el formal son la misma variable, y cualquier modificación en la función sobre el parámetro formal
implica que estamos modificando el parámetro real.
Para especificar en Pascal un parámetro por referencia se utiliza la palabra reservada VAR
antes de la definición del parámetro formal, como se hace en el siguiente ejemplo:
Como puede observar, ya no es una función sino un procedimiento. Y eso es lo que vamos a
estudiar a continuación. Veremos ejemplos del uso de parámetros por referencia en los ejemplos
de este tema, y sobre todo en el tema de tablas.
Procedimientos
Hemos estudiado que una función devuelve un valor como resultado de la función. Un procedimiento es
simplemente un módulo o subprograma que no devuelve nada como resultado de su ejecución.
Esta distinción no es del todo exacta: Un procedimiento no devuelve información como resultado de su
ejecución, pero si puede devolver información a la función que lo ha llamado mediante parámetros por
referencia. Definición de un procedimiento
Un procedimiento se define:
Procedure Suma (Real:a,b);
var
total: real;
begin
total := a + b;
write( a, ' + ', b, ' = ', total);
end;
Como podemos comprobar, es muy similar a la definición de una función: cambia la palabra reservada
function por procedure y no devuelve nada como resultado de la función. Esto implica que no es
necesaria una sentencia de asignación con el nombre de la función al final del procedimiento.
Invocación de un procedimiento
suma (7,5);
pero nunca
total := suma (7,5);(* Incorrecto *)
Observe este ejemplo: además del procedimiento suma se ha codificado un procedimiento sin parámetros
que hace una presentación inicial por pantalla. Descubra como se define e invoca dicho procedimiento sin
parámetros.
Intentemos ahora mediante ejemplos ver la diferencia entre parámetros por valor y referencia, y entre
funciones y procedimientos.
Ejemplos Prácticos
Quizás la parte más difícil de la programación sea, ante la resolución de un determinado problema,
decidir como se descompone en funciones o procedimientos.
La primera aproximación es separar tres grandes bloques: entrada de datos, cálculo de resultados y
presentación final de los resultados.
Seguir desglosando estos bloques en módulos depende de la complejidad del problema. Al mismo tiempo el
decidir si un módulo será función o procedimiento dependerá del algoritmo particular que vayamos a
implementar.
Ejemplo 1: Suma de dos elementos
Queremos implementar un modulo que reciba dos parámetros y devuelve la suma de los dos primeros.
Previamente hemos visto un procedimiento que sumaba dos elementos, y presentaba el resultado por
pantalla.
Lo que deseamos es modificar dicho ejemplo para que devuelva como resultado del módulo la suma.
Intente modificarlo usted mismo antes de ver la solución, que le explicamos a continuación:
Al iniciar la ejecución se definen las variables a, b y total, y se procede a solicitar al usuario los valores
de a y b.
A continuación se llama a la función:
Dentro de la función:
Se realiza la correspondencia: los parámetros reales se corresponden con los parámetros formales.
Observe: los parámetros formales tienen el mismo identificador que los reales. Pero no son los mismos:
los parámetros formales sólo existen dentro de la función, y se consideran locales a la función.
a := a+b;
Se suman a y b, y el resultado se almacena en el parámetro formal a .
suma := a;
El valor de la suma, almacenado en a, se devuelve como resultado de la función.
Al acabar la ejecución de la función, el resultado de la función se asigna a total.
Gráficamente:
Las variables a y b del programa principal, globales, tienen los mismos identificadores que los
parámetros formales a y b de la función suma.
Sabemos que los parámetros formales se consideran locales a la función. Cuando variables locales y
globales coinciden en el identificador las referencias válidas son las de las variables más "internas". Por lo
tanto "predominan" los parámetros formales y las variables locales sobre las variables globales.
Si deseamos usar una variable global dentro de una función o procedimiento que tiene una variable de
tipo local (o un parámetro) con el mismo identificador tendremos que cambiar alguno de los dos.
Por otra parte, hemos modificado el parámetro formal a dentro de la función suma, pero esto no
significa que se haya modificado el parámetro real a, ya que lo hemos pasado por valor. Para comprobar
este último punto puede utilizar trazas de depuración para presentar el valor de las variables globales a y
b después de la llamada a la función.
Modifique el programa para que la salida tras ejecutarse sea similar a la siguiente:
Ejemplo 2: Intercambio
En el ejemplo anterior hemos comprobado que al pasar un parámetro por valor a una función, es
imposible modificar el parámetro real dentro de la función: se transfiere una copia.
Veamos a continuación como es necesario pasar los parámetros por referencia para poder modificarlos
dentro de una función o procedimiento.
El siguiente programa intercambia el valor de dos variables: es necesario modificarlas dentro del módulo.
Para ello el modulo debe definir los parámetros formales por referencia (VAR). Vamos a analizar el
algoritmo de Intercambio.pas
Para comprobar todo esto, nada mejor nuevamente que utilizar trazas de depuración. Observe el resultado:
dentro de la función swap se modifican las variables globales x e y.
El código de la función swap queda:
aux : Integer;
Begin
if (dep) then
begin
end;
aux := y; (* almacena en aux el valor de y para que no se pierda en *) (* la siguiente asignación *)
y := x;
x := aux; (* Le asigna el valor de y que se guardo en aux *)
if (dep) then
begin
end;
End;
No olvide revisar el código de los ejemplos antes de pasar al tema siguiente, e intentar realizar los
ejercicios propuestos.
Este capítulo se puede seguir de forma secuencial o bien accediendo directamente al apartado que se
desee:
Introducción
Definición y uso
Tablas y cadenas de caracteres
Tablas multidimensionales
Introducción
Podríamos hacer una primera clasificación de los tipos de datos que maneja un ordenador: tipos de datos
simples y tipos de datos compuestos.
Simples: son aquellos que sirven para representar información de tipos elementales como por
ejemplo números enteros, reales o información de tipo carácter. Son los que hemos estudiado
hasta este momento.
Compuestos: son conjuntos de datos simples relacionados entre sí de alguna forma.
Dentro de los tipos de datos compuestos, podremos hablar de estáticos y de dinámicos. En los primeros el
tamaño de los datos está prefijado al iniciar el programa. En los segundos el tamaño de los datos puede
variar durante la ejecución del programa.
Definición
En este apartado vamos a introducir una estructura compuesta: las tablas, y en concreto las de carácter
monodimensional (una dimensión). Más adelante estudiaremos las tablas multidimensionales (más de una
dimensión).
Definimos tabla como un conjunto finito y ordenado de elementos homogéneos. Las tablas también
reciben el nombre de matrices o arrays.
En las tablas monodimensionales los elementos están ordenados desde el primero hasta el último,
pudiendo acceder a los mismos mediante un índice que indica su posición.
Una tabla se identifica por un nombre. Si especificamos dicho nombre sin hacer referencia al índice nos
estaremos refiriendo a la tabla entera, no a alguno de sus elementos.
Para acceder al contenido de un elemento de una tabla se deberá especificar el nombre de la tabla e
indicar mediante un índice la posición del elemento en cuestión.
El índice de una tabla podrá variar desde el límite inferior hasta el superior, que se especifican en la
definición de la tabla. Estos límites deberán ser constantes, y por lo tanto el tamaño de la tabla esta fijado
en el momento de la compilación.
Las tablas se utilizan para facilitar el acceso a información que puede ser agrupada siguiendo algún
concepto lógico de agrupación, y que además sea información homogénea, por ejemplo:
Las notas de un conjunto de alumnos: todas son del mismo tipo, reales, y podemos tener a los
alumnos ordenados alfabéticamente.
Los precios de los productos de una tienda: son todos del mismo tipo, y pueden ser ordenados por
el número de referencia.
Las medidas de un termómetro, o cualquier tipo de sensor o alarma, a lo largo del día: todas las
medidas serán del mismo tipo, y pueden ser ordenadas por la hora en que fueron tomadas
Para definir una tabla en Pascal se utiliza la palabra reservada array. Además necesitamos conocer:
Se pueden definir directamente variables de tipo tabla o utilizar la sección de tipos definidos por el
usuario:
Type
lecturas = array[1..24] of integer; (* tipo base: integer *)
Var
temperaturas : lecturas; (* Almacena temperaturas cada hora *)
humedad : lecturas; (* Almacena humedad cada hora *)
medias: array[1..31] of real;
i: integer;
Definimos en la sección Type un tipo definido por el usuario de nombre lecturas, que será una tabla de 24
valores de tipo entero. Una vez definido este tipo lecturas, podremos usarlo en las definiciones de
variables como cualquier tipo básico. Es lo que encontramos a continuación: la definición de dos
variables, temperatura y humedad de ese tipo.
En la misma sección definimos otra tabla de nombre medias, que almacena 31 valores de tipo real.
Observe la notación para especificar el valor inicial y final de los índices, encerrados entre corchetes y
separados por dos puntos.
Utilización
Para acceder a un elemento de la tabla simplemente necesitamos el nombre de la tabla y el índice de ese
elemento. A partir de ese momento lo podremos usar en cualquier expresión tal como usábamos las
variables de tipos simples, variables de tipo entero o real:
temperaturas[7] := 25;
media := (temperaturas[7] + temperaturas[8]) / 2;
O en operaciones de lectura y escritura:
write('Introduzca la temperatura al mediodia');
readln( temperaturas[12] );
write( 'La temperatura a las 12:00 es de ', temperatura[12] );
Tablas y bucles
Lo que hace realmente útil y potente a las tablas es la posibilidad de usarlas en bucles, en los que la
variable de control se utiliza al mismo tiempo como índice de acceso a los elementos de la tabla:
end;
En este bucle se solicita al usuario que vaya introduciendo las temperaturas medidas a lo largo del día. Y
para ello aprovecha la variable de control (o índice) para acceder a los distintos elementos de la tabla
temperatura.
Tablas y módulos
Los módulos (funciones y procedimientos) pueden recibir como parámetros tablas. Siguiendo los
ejemplos anteriores, observe esta función que recibe como parámetro una tabla (del tipo lecturas) y
devuelve como resultado de la función la media de las medidas realizadas.
Para calcular la media de un conjunto de valores se suman todos los valores, y la suma total se divide por
el número de valores. Para ello necesitamos una variable que acumule la suma, e iniciarla antes del bucle
a cero.
Function Media(medidas: lecturas): Real;
Var
i: integer;
suma: integer;
Begin
suma := 0;
for i:= 1 to 24 do
suma:= suma + medidas[i];
Media := suma/24.0
End;
No es posible en Pascal que una función devuelva como resultado de la función una tabla. Por lo tanto la
única manera de modificar una tabla dentro de una función o procedimiento es pasarla por
referencia. Puede repasar como se usan los parámetros por referencia o consultar el tema de
subprogramas.
Observe la siguinte función Lectura: se solicita al usuario que vaya tecleando los 24 valores de
temperatura y se van almacenando en los elementos de la tabla, que se ha pasado por referencia. Y de
nuevo utilizamos la variable de control del bucle como índice de los elementos de la tabla.
Procedure Lectura(VAR medidas: lecturas);
Var
i: integer;
Begin
writeln('Introduzca los valores(01:00 hasta 24:00');
for i:= 1 to 24 do
begin
write('Medida a las ', i, ': ');
readln(medidas[i]);
end;
End;
Existe otra limitación: es necesario definir un tipo de datos definido por usuario (sección Type) que se
corresponda con la tabla antes de definir la función y usarla como parámetro. La siguiente definición sería
errónea:
(* *)
(* !!!!!!!!!!!! Este procedimiento no compila !!!!!!!!!!!!!!! *)
(* *)
Procedure Lectura(VAR medidas: array[1..24] of integer);
Var
i: integer;
Begin
writeln('Introduzca los valores(01:00 hasta 24:00');
for i:= 1 to 24 do
begin
write('Medida a las ', i, ': ');
readln(medidas[i]);
end;
End;
Será necesario definir primero un tipo de datos que se corresponda con esa tabla y usar el identificador de
ese tipo creado por el usuario en el procedimiento:
Type
lecturas = array[1..24] of integer;
Maximo := max;
End;
La tabla medidas se pasa por valor: no hay que modificarla, sólo consultarla.
Se supone que el máximo es el primero elemento de la tabla.
A continuación se recorre el resto de la tabla. En el caso que algún valor sea mayor que le
primero, será nuestro nuevo máximo.
La última sentencia de la función es una sentencia de asignación. Recuerde: se le asigna al
nombre de la función el valor que deseamos que devuelva la función.
Repase todas estas funciones, y algunas más, en el programa tablas.pas. Observe que si compila este
ejemplo recibirá unos mensajes de aviso o atención, llamados warnings o avisos que puede observar en la
siguinte figura:
Nos avisa que hemos definido variables que luego no se utilizan en el código. Esto es debido a que dentro
de este programa hemos conservado la versión inicial para que compare como se va mejorando y
haciendo modular un programa.
Hemos introducido el concepto de tabla y definido las tablas monodimensionales y su uso. Más tarde
estudiaremos las tablas multidimensionales.
Antes vamos a detenernos en un tipo muy particular de tabla monodimensional: las tablas de caracteres,
llamadas cadenas de caracteres. Las cadenas de caracteres no son más que tablas unidimensionales cuyo
tipo base es char. Para facilitar su uso en Pascal se utiliza el tipo de datos string.
Definición
type
var
nombre = cadena40;
direccion = cadena80;
descripcion = cadena;
apellidos = string[50];
Como podemos observar es posible definir un tipo de datos string en la sección Type. O definir variables
de este tipo usando la palabra string directamente en la definición de la variable.
En este tipo no se define el valor inicial y final de la tabla: sólo se define el tamaño. El valor inicial se
supone siempre que es uno (1). Luego el tipo cadena40 significa que tenemos una tabla de 40 caracteres,
y el índice varía desde 1 hasta 40.
En este tipo es opcional definir el tamaño de la cadena: si no lo hacemos se supone que es 255. Hemos
definido cadenas de distintos tamaños en función de las posibles necesidades.
Acceso
Se puede acceder a los elementos de una cadena de caracteres de forma individual como en las tablas, o
hacer asignaciones directas utilizando el nombre de la variable:
apellidos[5] := 'L'; (* Acceso caracter a caracter *)
readln(apellidos[4]);
En el caso de la variable nombre, su tamaño máximo o físico es 40, y su longitud tras la asignación
nombre := 'Felipe Fernandez'; es de 16.
Realmente el tamaño físico de la tabla es superior al esperado. Hemos mencionado previamente que el
índice de la cadena puede oscilar desde 1 hasta el tamaño del string. El valor de índice cero (0) almacena
el tamaño lógico de la cadena de caracteres en cada momento. No es conveniente modificar este valor en
nuestro programa porque estaremos truncando nuestra cadena, y forzando una longitud lógica que no es la
real.
Limitaciones
En el tipo string existen limitaciones similares a las tablas: no es posible que una función devuelva como
resultado una cadena ni definir este tipo de datos en la cabecera de un módulo.
Por otra parte, los índices de las tablas pueden ser de cualquier tipo ordinal: enteros, carácter, subrango,
etc. Estudie el siguiente ejemplo, donde el índice de la tabla es de tipo carácter.
Se trata de solicitar al usuario una cadena de caracteres por pantalla y contar el número de repeticiones de
las distintas letras:
repeticiones: array['a'..'z'] of integer;
(* El índice indica la letra, y el valor que almacena el
número de repeticiones *)
...
Observe la definición de la tabla y su uso posterior en un bucle. El algoritmo lo que hace es recorrer la
cadena de caracteres uno a uno, e ir incrementando el elemento de la tabla repeticiones cuyo índice
coincida con el valor de la cadena de caracteres (cadena[i]).
long := ord(cadena[0]);
(* La longitud lógica de una cadena se *)
(* almacena en el elemento 0 *)
Observe resaltados tanto el cálculo de la longitud de una cadena de caracteres (elemento cero) como el
acceso a los elementos de la tabla repeticiones para incrementarlos en una unidad. El índice de la tabla
repeticiones es el carácter i de la cadena (cadena[i]), que es una letra.
El código completo de este ejemplo lo puede encontrar en TablasLetras.pas.
Intente mejorar este programa, creando procedimientos que hagan el cálculo de ocurrencias y la
presentación por pantalla de los resultados.
Las tablas multidimensionales se basan en el mismo concepto que las tablas monodimensionales: un
conjunto de elementos ordenado y homogéneo. Pero ahora el acceso a los elementos se hace mediante
más de un índice. De esta forma tendremos tablas bidimensionales (dos índices), tridimensionales (tres
índices), etc.
Definición
La definición de tipos o variables de este tipo es muy similar al de tablas unidimensionales. La única
diferencia es que tenemos varias dimensiones. Se define el rango de cada índice como en tablas
unidimensionales (inicio..fin), y cada rango se separa del siguiente por comas (i1..f1, i2..f2, i3..f3):
type
var
El acceso a los elementos de una tabla mutidimensional se hace especificando un índice para cada
dimensión, separados estos por comas dentro de los corchetes:
for i:=1 to 10 do
for j :=1 to 10 do
total := total + valor[i,j];
Es muy frecuente utilizar tablas de dos dimensiones para representar variables que son funciones de dos
parámetros distintos.
Esto es lo que se representa en la siguiente figura: tres filas por cinco columnas. En las posiciones de las
tablas hemos representado los índices necesarios para acceder a cada elemento de al tabla.
Ejemplos de posible utilización de tablas bidimensionales son:
Gastos mensuales clasificados por tipo de gastos: una dimensión puede representar los meses y
otra el tipo de gastos.
Ejemplo: ventas
Estudie el siguiente programa: intentamos que una tabla refleje las ventas por meses y por zonas de varios
agentes comerciales.
Necesitamos una tabla bidimensional: las filas representan los meses y las columnas las zonas.
Type
ventas = array[1..2, 1..4] of real;
(* Tabla para almacenar las ventas mensuales en cada región *)
(* Tenemos 12 meses (1..12) y 4 regiones (1..4) *)
Var
i: integer; (* Var. auxiliar para contadores *)
j: integer; (* Var. auxiliar para contadores *)
Cesar: ventas; (* Tabla que recoge las ventas de un
comercial 12 meses por 4 zonas *)
A continuación nuestro programa solicita al usuario los datos por meses y por zonas. Par ello debe
recorrer la tabla por filas y columnas. Es necesario un bucle que recorra las filas. Para cada iteración de
este bucle necesitamos un bucle interior que vaya recorriendo las columnas de esa fila.
(* Observe: para "llenar" la tabla bidimensional necesitamos dos bucles for anidados. El primero o
exterior recorre las filas, y el segundo o interior recorre las columnas *)
end;
end;
Un bucle similar se utiliza para presentar los valores de la tabla por pantalla. El código completo lo puede
encontrar en tablasmulti_01.pas.
No olvide un detalle: ¿cómo se le debe pasar la tabla a la función de lectura? Posiblemente ya lo haya
descubierto: por referencia.
Además, a las funciones de lectura y escritura le vamos a pasar como parámetro una cadena de caracteres
para avisar al usuario del comercial del que se trata.
i,j: integer;
begin
(* Observe: para "llenar" la tabla bidimensional necesitamos dos bucles for anidados. El primero o
exterior recorre las filas, y el segundo o interior recorre las columnas *)
end;
end;
end;
Observe que:
Como puede observar, la modularidad es un concepto muy importante a la hora de modificar o mejorar
las prestaciones de un programa.
Y ahora seguramente querrá hacer algo con los datos de ventas, como por ejemplo calcular el máximo de
ventas de cada vendedor. Vamos a implementar una función que reciba los datos de ventas de un
empleado y devuelva el máximo de dichas ventas.
(* Calcula el máximo de ventas (en todos los meses y en todas las zonas de un empleado que se le pasa
como parámetro por valor *)
Function max_ventas(empleado: ventas): Real;
Var
max: Real;
i,j: integer;
begin
max := -1.0;
for i:= Enero to Diciembre do (* recorre filas: Enero hasta Diciembre *)
begin
max := empleado[i,j];
end;
max_ventas := max;
end;
writeln('Maximos de ventas');
writeln('Cesar: ', max_ventas(Cesar):0:1);
writeln('Jesus: ', max_ventas(Jesus):0:1);
Observe como la llamada a la función max_ventas se realiza dentro de la función writeln. El código
completo se encuentra en el fichero tablamulti_02.pas. Si compila este programa obtendrá un nuevo
warning: de nuevo dejamos variables definidas pero no utilizadas. Elimine las definiciones de esas
variables y vuelva a compilar para que desaparezcan los avisos.
A partir de aquí las posibilidades son infinitas. Por ejemplo el tipo base de las tablas no tiene por que ser
un tipo básico: puede ser un tipo definido por el usuario. De esta forma las posibilidades se multiplican,
sobre todo cuando estudiemos en el tema siguiente los registros.
Vamos a proponer un nuevo cambio: crear una tabla unidimensional, donde cada uno de los elementos
sea una tabla del tipo ventas.
Type
ventas = array[Enero..Diciembre, 1..NumZonas] of real;
(* Tabla para almacenar las ventas mensuales en cada región *)
(* Tenemos 12 meses (1..12) y 4 regiones (1..4) *)
Var
plantilla: vendedores;
De esta forma no tendremos que modificar los procedimientos creados, sólo las llamadas o invocaciones a
estos procedimientos. Si a las funciones de los ejemplos anteriores se le pasaba como parámetro una tabla
del tipo ventas, ahora le pasaríamos como parámetro un elemento de la tabla vendedores. Cada uno de
estos elementos es del tipo ventas.
Las llamadas a las funciones de lectura y escritura del módulo principal quedarían de la siguiente forma:
max_todos := max;
end;
Con este ejemplo hemos terminado el tema de tablas. Pasemos ahora a un nuevo tema: los registros o
estructuras.
Este capítulo se puede seguir de forma secuencial o bien accediendo directamente
al apartado que se desee:
Introducción
Definición
Uso de registros
Tablas de registros
Introducción
A cada una de estas variables que forma parte del registro se le conoce como
campo (o atributo), y puede ser de cualquiera de los tipos explicados.
Los elementos de una tabla son todos del mismo tipo, es un conjunto
homogéneo.
Los elementos de un registro pueden ser de distintos tipos,
heterogéneos.
título
autor
editorial
año de publicación
etc.
referencia
código de barras
color
precio
descuento asociado
etc.
Definición
Al igual que ocurre con las tablas, podemos definir variables de tipo registro
directamente en la sección de variables, o definir un tipo de usuario registro. Para
definir un registro se usa la palabra reservada record. A continuación se definen
los distintos campos del registro junto con sus tipos. Esto es lo que podemos
observar en el siguiente ejemplo:
Type
persona = record
nombre : string;
edad : integer;
peso : real;
altura : real;
end;
Var
alumno : persona;
profesor : record
nombre : string;
edad : integer;
peso : real;
altura : real;
end;
alumno1, alumno2 : persona;
Para acceder a los elementos basta con utilizar el nombre de la variable, seguido de
un punto y el nombre del campo. De esta forma se puede usar ese campo como
una variable más en nuestro código:
write('Peso : ');
readln(alumno.peso);
write('Altura : ');
readln(alumno.altura);
ratio = alumno.peso/alumno.altura;
alumno1 := alumno;
Y además se debe cumplir no sólo que respondan a la misma estructura, sino que
el tipo asociado en sus definiciones sea el mismo. Según esto, la sentencia:
Por otra parte, no se pueden comparar dos registros de la forma habitual, con el
operador =
(* Compara dos registros del tipo persona campo a campo y devuelve TRUE o
FALSE *)
Function compara_persona(per1, per2 : persona): Boolean;
Begin
End;
if ( compara_persona(alumno1,alumno2) ) then
writeln('Alumno1 y alumno2 son iguales')
else
writeln('Alumno1 y alumno2 NO son iguales');
Aunque es posible asignar valores a los campos de un registro uno a uno, lo más
habitual es crear un procedimiento que se encargue de la lectura de todo el
registro. Observe en el siguiente ejemplo que es necesario pasar el parámetro por
referencia.
El uso de estas funciones es bastante sencillo. Además puede observar que el uso
de registros como parámetros de funciones es similar al uso de tablas.
read_persona(alumno2);
write_persona(alumno2);
Registros anidados
Es posible incluso que alguno de los campos de un registro sea a su vez otro
registro: en este caso es necesario que el registro "interior" se defina primero.
Type
Tpersona = record
(* Tipo que define las caracteristicas de una persona *)
nombre : string[TAMNOMBRE];
dni : string[TAMDNI];
edad : integer;
end;
Talumno = record
datos : Tpersona;
teoria : real;
practica : real;
end;
Var
Juan: Talumno;
Observe que el identificador de tipo se ha precedido con una 'T'. A veces se usa
esta notación para hacer más evidente que se esta usando un Tipo definido por el
usuario. Para acceder a los campos del registro anidado o interior se usa el
operador ".":
write('Nombre: ');
readln(alu01.datos.nombre);
write('Edad: ');
readln(alu01.datos.edad);
write('Nota Teoria: ');
readln(alu01.teoria);
write('Nota Practica: ');
readln(alu01.practica);
Estudiemos ahora una de las aplicaciones más frecuentes de los registros: las
tablas de registros.
Tablas de registros
Const
TAMCLASE = 10;
TAMNOMBRE = 30;
TAMDNI = 13;
Type
Tpersona = record (* Tipo registro que define las
características de una persona *)
nombre : string [TAMNOMBRE];
dni: string[TAMDNI];
edad: integer;
end; (* Tipo que define las caracteristicas de un alumno:
persona +notas *)
write('Nombre: ');
readln(miclase[i].datos.nombre);
write('Edad: ');
readln(miclase[i].datos.edad);
Una vez que tiene los datos de su clase querrá hacer algún tipo de cálculos con
ellos. Vamos a realizar como ejemplo una función que nos presente por pantalla la
edad media de la clase.
Para ello necesitamos que la función reciba como parámetro toda la clase. ¿Por
valor o por referencia? Recuerde: si no va a modificar el parámetro se debe pasar
por valor.
Debemos codificar un bucle que vaya recorriendo cada elemento de la tabla (la
clase) y sumando la edad de cada alumno. Finalizado el bucle, dividimos por el
número de alumnos. El código resultante es:
Introducción
Definición
Uso de ficheros
Ficheros de texto
Introducción
Todo lo estudiado hasta ahora sería poco útil si no existiese alguna forma de hacer
que nuestros datos se puedan guardar de forma permanente entre las distintas
ejecuciones de un programa.
Hasta ahora nuestros datos desaparecían tras utilizar un programa. Sin ir más
lejos, en los programas que gestionaban los alumnos de una clase, era bastante
tedioso tener que introducir todos los datos de la clase (nombres, notas, etc.) cada
vez que se necesitaba trabajar con nuestro programa.
nombre.ext
La extensión se utiliza para distinguir el tipo del contenido del fichero. De esta
forma, si vemos un fichero con extensión ".c", sabremos que se trata de un fichero
con un programa en lenguaje C. Y como ya hemos observado, los ficheros que
contienen programas en Pascal usan la extensión ".pas".
Como los soportes de almacenamiento secundario son más lentos que la memoria
principal, la forma de trabajar es leer del fichero los datos con los que se va a
trabajar y utilizarlos en la memoria principal.
Para usar los ficheros es necesario definirlos primero, y a continuación usar las
funciones y procedimientos que estudiaremos a continuación.
Definición
Un fichero es una secuencia de elementos del mismo tipo. Por lo tanto para definir
un fichero es necesario determinar primero el tipo base de ese fichero. El proceso
habitual es definir primero el tipo base, y a continuación el tipo fichero.
Const
TAMCLASE = 10;
TAMNOMBRE = 30;
TAMDNI = 13;
Type
Tpersona = record
(* Tipo que define las caracteristicas de una persona *)
nombre : string[TAMNOMBRE];
dni : string[TAMDNI];
edad : integer;
end;
Talumno = record
(* Tipo que define las caracteristicas de un alumno *)
datos : Tpersona; (* Registro anidado *)
teoria : real;
practica : real;
end;
Var
fichero : Tfichero;
Asignación
Es la operación de asociar un fichero físico a una variable del tipo fichero. Una vez
realizada esta asociación, utilizaremos la variable de tipo fichero para todas las
operaciones relativas al fichero físico.
assign(fichero, 'clase.dat');
Apertura
Una vez asignado un fichero a una variable tipo fichero, es necesario "abrirlo" para
permitir operaciones de lectura o escritura. Para ello es posible utilizar dos
procedimientos:
Escritura
Una vez abierto el fichero, podemos escribir en él datos del tipo base del fichero.
Para ello disponemos del procedimiento write, al que se le indica en que fichero se
escriben los datos: write(fichero, var1, var2, .. varn); Si aprovechamos el código
del programa de gestión de la clase podemos crear un bucle donde se vayan
guardando todos los alumnos en fichero:
Lectura
Partiendo del ejemplo anterior, una vez almacenada la clase en fichero, la podemos
recuperar con el bucle:
Una vez hayamos terminado de trabajar con el fichero, es necesario cerrarlo, con el
procedimiento close, cuyo único parámetro es el fichero:
close(fichero);
Otros procedimientos
filepos
A menudo es necesario conocer en que registro del fichero abierto estamos en cada
momento. Para ello se usa el procedimiento filepos, que recibe como parámetro la
variable de tipo fichero:
filepos(fichero);
Devuelve como resultado un entero que indica la posición del fichero en la que nos
encontramos. Los registros dentro de un fichero se numeran a partir de cero. Por lo
tanto, si tenemos tres registros en el fichero, se numeran de 0 a 2.
pos := filepos(fichero);
filesize
numelem := filesize(fichero);
seek
seek(fichero, posicion);
Si queremos modificar un registro que acabamos de leer de un fichero, como la
operación de lectura hace que avancemos al siguiente registro, deberemos
retroceder una posición antes de escribirlo de nuevo:
Lo que necesitamos es que nuestro programa sea capaz de leer los datos de
temperatura de un fichero para calcular medias, máximos y mínimos. Del mismo
tiempo debe ser posible guardar los datos en un fichero para su posterior lectura.
El menú principal de nuestro programa tendrá una estructura similar a:
writeln;
writeln(' 1. Lectura de datos');
writeln(' 2. Presentacion de medidas');
writeln(' 3. Calculo de media, maximos y minimos');
Nos creamos una función que se encarga de guardar los datos en fichero, donde
hemos destacado en negrita los procedimientos relacionados directamente con
ficheros: assign, rewrite, write y close.
assign(fichero, nombre);
rewrite(fichero);
close(fichero);
End;
Del mismo modo creamos una función que lea los datos de fichero con los
procedimientos assign, reset, read y close.
También se usa la función eof, que recibe como parámetro un fichero y devuelve si
hemos alcanzado o no el final de ese fichero, y por lo tanto ya no hay más datos:
assign(fichero, nombre);
reset(fichero);
close(fichero);
End;
Vuelva a ejecutar el programa. Antes de hacer nada, presente los datos (opción 2).
Evidentemente no hay datos, están a cero. Use ahora la opción leer de fichero.
Teclee el nombre previamente elegido, y compruebe que están los datos tal como
usted los introdujo.
Ficheros de texto
Los archivos de texto se organizan en líneas separadas por el carácter fin de línea
(end of line, eol). Por lo tanto un fichero de texto es un conjunto de líneas de
distinta longitud separadas por el carácter eol. El final del fichero, al igual que en
los ficheros con tipo, se marca con eof (end of file).
Tambien se utilizan este tipo de ficheros cuando es necesario que los datos se
puedan visualizar con editores de texto. Los procedimientos para definir, leer,
escribir y cerrar ficheros son muy similares a los de ficheros con tipos.
Definición
En los ficheros de texto el tipo base del fichero es el tipo char. Los ficheros de texto
se definen directamente con la palabra reservada Text:
Program Lab02;
Const
primhora = 1;
ulthora = 3; (* 24 *)
Type
lecturas = array[primhora..ulthora] of integer;
Tfichero = text;
Var
fichero : Tfichero
fichero1 : Text;
assign(fichero, 'clase.txt');
reset(fichero);
rewrite(fichero);
close(fichero);
Escritura
Aquí aparecen las primeras diferencias con los ficheros con tipo. Podemos escribir o
almacenar datos en el fichero de distintos tipos y con distintas longitudes. Recuerde
que en los ficheros con tipos todos los datos deben ser del mismo tipo: del tipo
base del fichero.
write(fichero, var1, var2, ... varn);
writeln(fichero, var1, var2, ... varn);
/* guarda caracter nueva línea tras variables */
Donde var1, var2, ... varn pueden ser de distintos tipos.
Modificando el ejemplo de las medidas atmosféricas:
Observe:
Lectura
Enlace a Dev-Pascal.