Pascal
Pascal
Veremos aquí los fundamentos del lenguaje en que se basa Delphi: la extensión por Borland del lenguaje
Pascal, llamada Object Pascal por su capacidad de manejar objetos.+
Hay muchos agregados en Delphi al lenguaje original; no obstante, se mantiene la filosofía que dio origen al
lenguaje, es decir, la estructuración en bloques. Con las extensiones de la Programación Orientada a Objetos
se extiende considerablemente el espectro de las aplicaciones que podemos escribir, manteniendo la
facilidad y velocidad que hicieron del Turbo Pascal uno de los lenguajes más difundidos.
Nota: en todos los ejemplos se utiliza Obtener(lista de variables) para indicar que las variables listadas
obtienen sus valores desde el mundo exterior -en DOS utilizaríamos Read, en Windows un Editor por
ejemplo. En forma análoga, para indicar que se muestra un texto en pantalla utilizamos Mostrar(lista de
valores o variables).
Delphi y Pascal
Delphi es un Entorno de programación, no un lenguaje. Son las ventanas y herramientas que tenemos
disponibles. Está basado en el lenguaje Object Pascal, que se utiliza para escribir el código que se ejecutará
en respuesta a los distintos eventos del sistema1.
Delphi reduce al mínimo la cantidad de código que es necesario escribir para construir un programa, gracias
a la utilización de componentes. Estos son objetos que tienen ya un comportamiento preprogramado, que
nosotros utilizamos como ladrillos para armar las aplicaciones. No hay que escribir nada para crear una lista
de palabras de la que el usuario puede elegir una; simplemente tomamos el componente que representa una
lista y lo ponemos en la ventana que deseemos, e indicamos las palabras que aparecerán. Todo sin escribir
código.
Si todo fuera tan fácil... sería muy aburrido, no? Y los programadores nos quedaríamos sin trabajo. Las
tareas repetitivas se pueden programar de antemano, como por ejemplo hacer que un botón se “hunda”
cuando se hace click sobre él. Pero para indicar qué debe hacer nuestro programa al hundirse el botón... eso
ya es otro cantar. Nadie más que nosotros puede escribir eso; y para ello necesitamos un lenguaje. Delphi
utiliza el lenguaje Pascal, pero podemos encontrar otros entornos muy parecidos que utilizan C++ (C++
Builder) o Java (JBuilder), por ejemplo. Este código se ejecuta en respuesta a los eventos que ocurren en el
sistema, como por ejemplo la anterior pulsación del botón.
El lenguaje Pascal
En un lenguaje no estructurado (como BASIC) un programa es una secuencia de líneas. En un lenguaje
estructurado (como Pascal), el programa se compone de una serie de bloques o estructuras. A su vez, cada
una de estas estructuras se compone de líneas de código.
En un lenguaje no estructurado, el flujo de la ejecución pasa de una línea a la siguiente; a menos que la
instrucción actual indique un salto o bifurcación a otra línea diferente, alterando la secuencia de órdenes.
En un lenguaje estructurado, se ejecutan secuencialmente los bloques. A su vez dentro de los bloques se
ejecuta línea por línea, pero sin posibilidad de “saltar” de un bloque a otro en cualquier momento. Debemos
terminar la ejecución de un bloque para pasar al siguiente.
1
Recordemos que los eventos son las cosas que suceden en el entorno: el movimiento del ratón, la
presión de una tecla, etc.
Object Pascal
Tenemos la posibilidad de anidar bloques, pero siguiendo a rajatabla la regla que dice que cada bloque
debe estar contenido completamente dentro de otro, o totalmente fuera. No se puede dejar “una pata”
del bloque dentro de otro y la otra afuera!
Podemos llamar a un bloque desde otro, pero este segundo bloque deberá terminar su ejecución antes de
volver al original que lo llamó; esencialmente, podemos considerar que el segundo bloque estaba dentro del
primero.
El siguiente gráfico muestra la diferencia fundamental entre los dos tipos de lenguajes:
Lenguaje no estructurado Lenguaje estructurado
Bloque1
Sentencia 1
Sentencia 2 Bloque2
Secuencia normal,
Sentencia 1 bloque por bloque
Secuencia normal, Sentencia 2 Sentencia 1 Bloque3
linea por linea Sentencia 2
Sentencia 3 En cualquier momento
podemos saltar a Sentencia
cualquier línea
No podemos saltar a
cualquier línea,
Sentencia 29 tampoco a
Sentencia 30 cualquier bloque
Sentencia 1
Sentencia 2
Sentencia 165
Sentencia 1
C Traducción “simultánea”, a medida que se leen las líneas: se interpreta cada una y se actúa en
consecuencia, inmediatamente. Sólo tenemos que escribir el programa y ejecutarlo. No obstante, la
ejecución es relativamente lenta debido al proceso de interpretación de lo escrito. El ejemplo más
conocido es el BASIC. Este modo de ejecución se denomina “interpretado”, y el programa que lo
hace intérprete.
C Traducción en un paso previo a la ejecución: este proceso se denomina compilación y el
programa que lo hace compilador. Es necesario compilar el programa entero con cada cambio, lo
que en ocasiones puede demorar bastante el proceso. La gran ventaja de este tipo de lenguajes es la
velocidad de ejecución; dado que el procesador no debe consumir tiempo traduciendo las líneas
2
Existe también lo que podríamos considerar una tercera forma de compilación, a medio camino
entre las otras dos: la compilación de un código intermedio independiente del procesador entre el código
máquina y el lenguaje de alto nivel, ejecutado por un núcleo intérprete especial. Esta es la técnica utilizada
por Java.
2
Ernesto Cullen
Pascal (el lenguaje que utiliza Delphi) es un lenguaje compilado. No obstante, el compilador es sumamente
rápido (una de las características que hicieron famoso a Borland); la generación del ejecutable no demanda
mucho tiempo y permite probar rápidamente los cambios introducidos. Además, como veremos luego, el
programa se divide en unidades funcionales, cada una de las cuales se compila por separado. Al modificar
una parte del programa, sólo hay que recompilar las unidades que se vean afectadas; esto acelera aún más el
proceso de traducción.
Ejemplo:
Un programa de nombre Tabla3 debería tener al menos las siguientes líneas:
program Tabla3;
begin
end.
Listado 2: Un programa
mínimo
Este programa no hace nada; sin embargo, el compilador creará un archivo ejecutable sin señales de error.
Ahora seguramente se estarán preguntando... ¿eso se puede hacer en Delphi? La respuesta es SI. No es el
tipo de programas que uno espera crear con Delphi, pero tenemos todavía la opción de hacerlo3. Se
denominan aplicaciones de consola, y en Windows se abrirá una ventana de DOS (se me pianta un
lagrimón) donde correrá nuestro programa.
Veremos luego como crear este tipo de programas4.
3
Hay un caso concreto de utilización de este tipo de programas: las aplicaciones CGI para Internet.
4
Este tipo de aplicaciones es muy indicado para los programas que se ejecutan sin intervención del
usuario; por ejemplo, un procedimiento que únicamente procese datos de un archivo y deje los resultados en
otro archivo (como los programas CGI de Internet).
3
Object Pascal
Simples-Ordinales
LongWord (32) Nro. Entero Sólo Delphi4: equivalente a Cardinal (32 bits,
sin signo)
Simples-Reales
5
El tipo Real original de Borland Pascal (48 bits) no es nativo en los coprocesadores matemáticos
de la familia Intel, por lo que las operaciones con ellos son más lentas que con otros tipos como el Single.
Esto se ha remediado en Delphi4 con la redefinición del tipo real a 64 bits (equivalente por lo tanto al tipo
double). El tipo anterior se llama ahora Real48. Por razones de compatibilidad se puede hacer que la palabra
real refiera al tipo viejo de 48 bits, con la directiva del compilador {$REALCOMPATIBILITY ON}. Por
defecto esta opción está desactivada.
6
El tipo Comp es un tipo nativo de los coprocesadores matemáticos de la familia Intel, que contiene
un número entero; no obstante, no se puede manipular como un ordinal (por ejemplo, no se puede utilizar
como parámetro de las funciones Inc o Dec) por lo que se incluye entre los reales. Utilice el tipo Int64 para
mejor desempeño.
4
Ernesto Cullen
Cadena
Fecha / Hora
Estructurados
Array
Record
Class
Class Reference
Set
File
Punteros
El lenguaje Pascal exige una concordancia exacta en los tipos de datos que intervengan en una misma
expresión. Esto significa que no podemos por ejemplo sumar una palabra con un número (¡algo que por otro
lado nos suena bastante lógico!). Se dice que es un lenguaje fuertemente tipificado. Veremos enseguida
algunas funciones pensadas para convertir datos de un tipo a otro -siempre que sea posible, por supuesto;
por ejemplo, podemos transformar un número real en un entero mediante un redondeo al entero más
cercano.
En los tipos numéricos se puede asignar un valor de un tipo a una variable de un tipo superior, que lo
contenga. Por ejemplo podemos asignar un entero a una variable de tipo real (los números enteros también
son reales) pero no a la inversa.
7
El tipo Currency almacena un número decimal pero en forma exacta, sin redondeos. Cuando se
pasa a una variable de otro tipo real se multiplica o divide por 10000 (4 cifras decimales). Por ejemplo: el
número 0.234 se almacena como 2340. Esto evita los errores de redondeo.
8
El tipo Variant puede contener diversos tipos de datos; fue introducido en Delphi 32 por exigencia
de la automatización OLE. Es más flexible pero más lento que los tipos comunes.
5
Object Pascal
Variables y Constantes
Los datos de un programa se almacenan en posiciones de memoria. Dependiendo si esos valores pueden
cambiar en el transcurso de un programa o no, Pascal clasifica estas posiciones como Variables o
Constantes respectivamente. Además, para identificarlas, el lenguaje exige que se les de nombre en el
programa. Estos nombres deben seguir ciertas reglas:
Las variables o constantes deben ser declaradas antes de ser utilizadas, en secciones destinadas a tal efecto:
la cláusula var indica que comienza una zona de declaración de variables, mientras la cláusula const se
usa para especificar la zona de declaración de constantes.
Al declarar las variables o constantes debemos especificar claramente el tipo de datos que contendrán,
porque el compilador necesita esta información para traducir el programa a código de máquina.
Ejemplo:
En el programa Tabla3 utilizaremos una variable de tipo entero para indicar la línea de la tabla que estamos
calculando; la llamaremos a. Entonces, agregamos al programa anterior la sección de declaración de
variables indicada por la cláusula var:
program Tabla3;
var
a: integer;
begin
end.
var var
a, b: integer; a: integer;
b: integer;
begin
end. begin
end.
6
Ernesto Cullen
variable1 := 67;
Pero hay veces que no basta con una línea. Por ejemplo, si queremos tomar un valor que ingresa el usuario,
multiplicarlo por una constante y mostrar el resultado en pantalla. En este caso debemos crear una
instrucción compuesta: un bloque begin..end. En los listados 22 y 23 se ven las mismas líneas; en el primer
caso son tres instrucciones distintas, mientras que en el segundo forman un solo bloque que se trata como
una instrucción compuesta. Hay ocasiones en que tendremos la obligación de agrupar las sentencias en un
bloque.
Las instrucciones compuestas se pueden utilizar cuando se espera una simple. En este caso, el end final va
seguido de un punto y coma. Begin nunca lleva punto y coma.
Ejemplo:
La sentencia if..then va seguida de una sola instrucción. Si debemos ejecutar varios pasos, debemos
crear una instrucción compuesta encerrando el bloque entre begin y end. Lo mismo ocurre con las
sentencias iterativas for..do, while..do que veremos después.
Asignación e igualación
En Pascal se distingue la asignación de la comparación de igualdad. La primera es la acción de dar valor a
una variable, mientras que la segunda es la operación de comparar dos valores.
Se utilizan diferentes símbolos: igual (=) para la comparación, que devuelve un valor lógico indicando si los
valores son iguales o no, y dos puntos igual (:=) para la asignación de un valor a una variable. Veamos un
ejemplo:
En diagramas de flujo se utiliza el rectángulo para representar una asignación. Por ejemplo, la asignación
anterior es equivalente a
a := 5
Las expresiones de Pascal pueden utilizar cualquiera de los símbolos convencionales para las operaciones
básicas: +, -, *, /, además de funciones (porciones de código) que devuelvan un valor de tipo compatible.
Ejemplo
Dadas las siguientes variables
var
a: integer;
b: real;
c: single;
7
Object Pascal
En los dos últimos casos, no se podría asignar el resultado de la operación a la variable a porque por
definición, las funciones utilizadas devuelven valores reales.
Funciones
Vemos en el ejemplo anterior la utilización de funciones: éstas no son más que pequeños programas, a los
cuales asignamos un nombre (por ejemplo, “sin”) y devuelven un valor. Es decir, se pueden usar en la parte
derecha de una asignación mientras el tipo de la variable de la parte izquierda sea compatible.
Eventualmente, las funciones pueden necesitar algún dato para procesar; estos datos se entregan a la función
en el momento de invocarla, encerrados entre paréntesis y separados por comas. Se denominan parámetros
de la función.
Ejemplo: la función Sin espera un dato de tipo real, que representará el ángulo al cual queremos calcularle
el seno, en radianes. Entonces, debemos invocarla con un número real (puede ser una variable o una
constante, o incluso otra función) entre paréntesis: Sin(30*Pi/180), Sin(Cos(2.876)), Sin(0.654).
Dado que las funciones siempre devuelven un valor, se pueden utilizar directamente como parámetros. Las
siguientes secuencias son equivalentes:
Var
a, b: real;
begin
a:= Sin(25*Pi/180); {a es igual al seno de 25 grados}
b:= Sqrt(a); {b es igual al cuadrado del seno de 25 grados}
Var
b: real;
begin
b:= Sqrt(Sin(25*Pi/180)); {b es igual al cuadrado del seno de 25 grados}
Vemos que entre paréntesis se declara una variable: esta variable existe únicamente dentro de la función, e
indica el valor que tenemos que entregar a la función para su proceso. En este caso, cualquier valor que
pasemos a la función entre los paréntesis se asignará dentro de la función a una variable X. A nosotros,
usuarios de la función, no nos interesan los nombres que ésta les asigna a sus parámetros; únicamente
tenemos que tener cuidado de enviar el tipo correcto de dato para cada parámetro.
El tipo de dato que se indica después de los dos puntos finales nos dice qué tipo de datos devuelve la
función; siempre es uno solo.
Las funciones pueden esperar más de un parámetro; en este caso, en la definición se listan todos y cuando la
llamamos debemos enviar cada dato en el orden que se define. Por ejemplo:
function FormatDateTime(const Format: string; DateTime: TDateTime): string;
Esta función espera dos datos: uno de tipo cadena (string, luego veremos qué significa el modificador const)
y el otro de tipo TdateTime. Devuelve una cadena de caracteres que contendrá la fecha y hora formateados
como se indique mediante códigos especiales en la cadena Format. Por ejemplo: suponiendo que fecha es
una variable de tipo TdateTime que tiene la fecha 1/1/1970,
8
Ernesto Cullen
Delphi trae definidas un montón de funciones como las anteriores. Están organizadas en distintos módulos
-unidades- por lo que para utilizarlas debemos incluir la unidad correspondiente en nuestro código. Veremos
cómo hacer esto cuando hablemos de las unidades.
9
Como ya dijimos, es posible asignar un valor entero a una variable de tipo real. En general, si el
tipo del receptor contiene al valor es válida la asignación. Así, asignar un valor real a una variable entera no
es correcto porque el tipo real posee más información que el entero, y el compilador no sabe qué parte
descartar; hay que convertir el dato.
9
Object Pascal
if condición then
sentencia_si_cumple
else
sentencia_si_no_cumple;
Falso
(Else)
La condición puede ser cualquier expresión que devuelva un valor lógico booleano (verdadero o falso).
Cuando la expresión es verdadera (la condición se cumple) se ejecuta la sentencia siguiente. Esta puede ser
simple o compuesta (un bloque begin..end), pero debe ser una sola.
Cuando la condición no se cumple y se especifica una acción alternativa (opcional) con la sentencia else,
ésta se ejecuta en lugar de la primera. Igual que antes, sólo debe haber una instrucción (simple o compuesta)
siguiendo a else.
NOTA: en los siguientes ejemplos, observe el uso del punto y coma final sólo después de la última
sentencia. Al momento de encontrar este signo de puntuación (si no se está dentro de una sentencia
compuesta) el compilador asume que termina la instrucción if. Por lo tanto antes del else no se va el punto
y coma. De lo contrario el compilador terminaría la orden if en ese momento y se encontraría con una
sentencia else que no tiene significado por sí sola.
Ejemplos:
if a=5 then
mostrar ('a es igual a 5')
else
mostrar ('a es distinto de 5');
if a<>5 then mostrar ('a es distinto de 5') else mostrar ('a es igual a 5');
if (a-5)>10 then
begin
mostrar ('a es mayor que 15');
mostrar ('el valor de a es ',a);
end
else
begin
Mostrar ('a es menor que 15');
mostrar ('el valor de a es ',a);
b:= a*a;
mostrar ('el cuadrado de a es ',b);
end;
10
Ernesto Cullen
if a>10 then
Mostrar('a es mayor que 10');
Cuando debemos seleccionar uno solo de una serie de valores del mismo tipo -siempre que estos valores
sean ordinales- podemos utilizar otra sentencia de selección: case. La sintaxis es la siguiente:
case selector of
valor1: sentencia1;
valor2: sentencia2;
:
valorN: sentenciaN;
else
sentenciaPorDefecto;
end;
El selector es una variable de un tipo que se pueda enumerar como por ejemplo integer, boolean, etc.
Según el valor que tome esta variable se ejecutará la sentencia que corresponda, la cual puede ser simple o
compuesta. Si el valor del selector no está contemplado en la lista de valores, se ejecuta la sentencia que
sigue a la cláusula else (opcional). La estructura termina con la instrucción end.
Se puede indicar un rango de valores escribiendo el primer valor seguido por dos puntos y luego el último
valor del rango:
var
n: integer;
begin
Obtener(n);
case n of
1..10: Mostrar('El número ingresado esta entre 1 y 10');
11..20: Mostrar('El número ingresado esta entre 11 y 20');
0: Mostrar('El número ingresado es cero');
else
Mostrar('El número ingresado es mayor que 20');
end;
end.
También se puede indicar la misma acción para varios valores, simplemente listándolos en la misma línea:
var
n: integer;
begin
Obtener(n);
case n of
1,3,5,7,9: Mostrar('El número ingresado es impar');
2,4,6,8: Mostrar('El número ingresado es par');
0: Mostrar('El número ingresado es cero');
else
Mostrar('Error! El número debía ser de una sola cifra');
end;
end.
Ejemplos:
case a of
1: mostrar('a vale 1');
2: mostrar('a vale 2');
3: begin
mostrar ('a vale 3');
b:= a*2;
mostrar ('el doble de a es ',b);
end;
else {esto es opcional}
mostrar ('a no es 1, 2 ni 3');
end;
11
Object Pascal
var
a: char;
begin
Obtener(a); {Una letra, por ejemplo una pulsación de tecla}
case a of
'a','e','i','o','u': Mostrar('La letra es una vocal');
else
Mostrar('No es una vocal');
end;
end.
Var
edad: integer;
begin
Obtener(edad);
case edad of
20: Mostrar('Ud. tiene 20 años');
30: Mostrar('Ud. tiene 30 años');
31..150: Mostrar('Ud. tiene más de 30 años');
end;
end.
For
La instrucción for tiene la siguiente sintaxis general:
o también
for variable:= valor_superior downto valor_inferior do
sentencia;
C se asigna el valor (inferior o superior según la variación elegida) inicial a la variable de control;
luego
C se compara el valor de la variable con el indicado a continuación de “to” o “downto”.
C si la variable es menor o igual (en la primera forma) o mayor o igual (en la segunda forma) que el
valor dado, se ejecuta la sentencia -que puede ser simple o compuesta y se incrementa (en la primera
forma) o se decrementa (en la segunda forma) la variable.
C se vuelve al paso (2) hasta que se pase el límite.
12
Ernesto Cullen
Variable
= Fin bucle
valor final
Sentencia
Modificar variable
NOTAS:
• Esta instrucción ejecuta una sola sentencia, que puede ser simple o compuesta.
• La variable debe ser de tipo ordinal.
• Al contrario de otros lenguajes, no es posible especificar un paso diferente de uno.
• La comprobación del límite se produce antes de ejecutar la sentencia interna, por lo que puede ser
que el bucle no se ejecute nunca.
Ejemplo:
Este bucle imprime la tabla del 3, del 1 al 10:
for a:= 1 to 10 do
mostrar (a,a*3);
la instrucción siguiente no hace nada, ya que la condición de terminación se verifica antes de ejecutar la
sentencia interna:
for a:= 2 to 1 do
mostrar(a);
Notemos que en este último ejemplo fue necesario utilizar begin..end porque dentro del bucle se debían
ejecutar dos sentencias.
13
Object Pascal
While
La sentencia while ejecuta una instrucción mientras se cumpla una condición. La comprobación se hace
antes de ejecutar la instrucción, por lo que puede ser que el bucle no se ejecute nunca. La sintaxis es:
¿se cumple Si
la condición? Sentencia
No
Fin bucle
Ejemplo:
La misma tabla del 3 del primer ejemplo del bucle for se escribiría de la siguiente manera utilizando
while:
a:= 1;
while a<=10 do
begin
mostrar(a,a*3);
a:= a+1;
end;
Note la necesidad de inicializar la variable de control antes de entrar al bucle; además, se puede controlar el
incremento dentro del ciclo. ¿Cómo haría la tabla en orden inverso, del 10 al 1?
Repeat
El bucle repeat ejecuta una serie de sentencias hasta que la expresión de control sea verdadera. Es, por lo
tanto, una especie de complemento del bucle while.
La sintaxis es
repeat
sentencia1;
sentencia2;
sentencia3
:
sentenciaN;
until condición;
14
Ernesto Cullen
Se comienza el bucle ejecutando las sentencias (no se comprueba la condición hasta el final) y al llegar a la
cláusula until (hasta) comprueba si se cumple la condición. Si se cumple (la condición devuelve verdadero)
se sale del bucle; caso contrario se repite desde la sentencia 1.
Aquí tampoco existe una variable de control obligatoria; la condición puede estar dada por un valor
particular de las coordenadas del ratón, por ejemplo.
Diagrama:
Sentencia1
Sentencia2
SentenciaN
Si
¿se cumple
la condición? Fin bucle
No
Ejemplo:
La misma tabla del 3 se puede realizar con un bucle repeat de la siguiente manera:
a:= 1;
repeat
mostrar (a,a*3);
a:= a+1;
until a>10;
Notemos que aquí también es necesario inicializar la variable de control, y se puede manejar su
comportamiento en cada ciclo. ¿Cómo haría la tabla en orden inverso, del 10 al 1?
15
Object Pascal
Tipos estructurados
Los tipos que hemos visto hasta ahora (integer, real, string, etc.) son tipos simples: cada uno tiene un rango
de valores posibles y almacena una sola clase de datos. Ahora estudiaremos otros tipos de datos más
complejos, llamados en general tipos estructurados porque poseen una cierta estructura, en tanto que pueden
contener más de un valor, a veces de distintos tipos.
Podemos definir alias o nombres alternativos para los tipos estándar; por ejemplo, una equivalencia muy
utilizada en Delphi es
type
tHandle = word;
A partir de esta definición, es posible declarar variables de tipo tHandle, que serán equivalentes
internamente al tipo word:
var
Manejador1: tHandle;
Manejador2: word;
En las instrucciones anteriores se declaran dos variables que internamente son equivalentes. Incluso es
posible utilizar una cuando se espera el tipo de la otra. Por ejemplo si una operación se realiza sólo con
datos tipo word, podemos utilizar en ella la variable Manejador1. La siguiente instrucción es válida
(NuevoManejador es de tipo word o THandle):
NuevoManejador:= Manejador1+Manejador2;
Esta característica de "renombrar" tipos existentes es muy útil al convertir programas de otro lenguaje, o al
adaptar los tipos utilizados por un sistema operativo, programado en otro lenguaje y que por lo tanto utiliza
las definiciones de los tipos de este último (en el caso de Windows, el lenguaje C). Por ejemplo, en C existe
un tipo uint que referencia números de enteros sin signo; el equivalente Pascal sería el word. Por lo tanto,
definiendo una equivalencia
type
uint = word;
podemos utilizar el mismo identificador en nuestro programa. Esto simplifica enormemente la conversión.
Registros
16
Ernesto Cullen
Un registro es un conjunto de campos. Los campos son variables internas del registro, que sólo tienen
existencia dentro de éste. Los campos pueden ser de cualquier tipo estándar o definido.
Los registros son una forma cómoda y conveniente de agrupar variables relacionadas.
Veamos un ejemplo:
Debemos almacenar los datos de tres personas. Por cada una tenemos Nombre, Apellido, Dirección,
Teléfono, Sexo (M o F) y Edad.
Una forma de hacerlo sería la siguiente:
var
Nombre1, Nombre2, Nombre3: string;
Apellido1, Apellido2, Apellido3: string;
Direccion1, Direccion2, Direccion3: string;
Telefono1, Telefono2, Telefono3: string;
Sexo1, Sexo2, Sexo3: char;
Edad1, Edad2, Edad3: integer;
Luego cuando asignamos los datos de la primera persona debemos utilizar Nombre1, Apellido1,
Direccion1, Telefono1, Sexo1, Edad1. Para la segunda persona usamos las variables con el
número 2, etc. De esta manera tenemos una serie de variables "sueltas" que se unen por programa;
únicamente nosotros sabemos que están relacionadas.
Mejor sería si pudiéramos indicar al compilador que mantenga las variables correspondientes a cada persona
juntas, de manera tal que trabajemos con la persona como un todo y no con las partes sueltas. Podemos
lograr esto definiendo un nuevo tipo de dato que contenga las variables de cada persona en una sola
estructura: un registro.
Entonces la solución al problema planteado quedaría como sigue:
type
tPersona = record
Nombre: string;
Apellido: string;
Direccion: string;
Telefono: string;
Sexo: char;
Edad: integer;
end;
var
Persona1, Persona2, Persona3: tPersona;
Ahora tenemos sólo tres variables; cuando hablamos de los datos de la 1ra. persona hacemos referencia a la
variable Persona1, cuando hablamos de la segunda a Persona2, etc. Es más conveniente y práctico que
la forma anterior10.
Bien, con los datos organizados de esta manera es más fácil trabajar. De acuerdo. Pero ¿cómo accedemos a
los campos de un registro?
Las variables (campos) se reconocen por su nombre; pero ahora hay que indicar también el nombre del
registro al que pertenece. Estas dos partes se separan con un punto (de forma similar a los nombres de
10
Notemos la convención (no regla) de los nombres en Delphi: los nombres de nuevos tipos de datos
comienzan con la letra T.
17
Object Pascal
archivo y las extensiones en DOS). En el ejemplo anterior, para asignar datos a la primera persona podemos
hacer:
Persona1.Nombre:= 'José';
Persona1.Apellido:= 'Pérez';
Persona1.Direccion:= 'Avda. Rivadavia 5476';
Persona1.Sexo:= 'M';
Persona1.Edad:= 34;
Resulta un poco tedioso el escribir el nombre del registro siempre adelante del nombre de la variable. Para
estos casos, hay una estructura de Pascal que nos alivia la tarea, el bloque with
El bloque with
El bloque with permite ahorrar un poco de escritura. La sintaxis general es
La parte repetida es lo que se quiere reemplazar, sin el punto. En el caso del ejemplo anterior tenemos:
with Persona1 do
begin
Nombre:= 'José';
Apellido:= 'Pérez';
Direccion:= 'Avda. Rivadavia 5476';
Sexo:= 'M';
Edad:= 34;
end;
Esta construcción se utiliza en cualquier caso que tengamos una parte repetida, no sólo para registros; por
ejemplo, es muy común en Programación Orientada a Objetos que haya que repetir el nombre del objeto al
acceder a distintos métodos o propiedades internas. También en estos casos se puede utilizar with.
Por ejemplo, si tenemos que escribir
Form1.Canvas.Pen.Color:= clRed;
Form1.Canvas.Brush.Style:= bsDiagonal;
Form1.Canvas.rectangle(10,10,200,200);
Vectores y matrices
En Pascal se pueden definir fácilmente dos tipos de datos muy utilizados como son los vectores y matrices.
Un vector es un arreglo de datos de un solo tipo que forman una serie ordenada. Podemos representarlo
18
Ernesto Cullen
Los distintos elementos se referencian con su número de orden, que llamaremos índice. En el lenguaje
Pascal se indica el índice entre corchetes ([ ]). También hay que indicar al compilador la cantidad de
elementos que tendremos en el arreglo y qué índice tienen el primer y el último elemento. Esto se hace en la
definición del tipo.
Type
tDigitos = array [0..9] of integer;
Esta instrucción dice al compilador que he definido una estructura de 10 (del 0 al 9) enteros (of integer)
llamada TDigitos. Para usar este tipo de datos debemos declarar una variable:
var
Vector1: tDigitos;
El primer elemento de este vector se referencia como Vector1 [0] y el último como Vector1 [9]. Por lo
tanto, para almacenar el valor 524 en la posición 2 (la tercera, porque empezamos desde 0) haríamos
vector1[2]:= 524;
El hecho que los elementos de un vector se referencien con un índice numérico nos permite utilizar bucles.
Por ejemplo, para almacenar los números del 0 al 9 en el vector anterior, haríamos:
¿Podemos hacer una estructura como ésta con un registro? Claro que sí; la mayor diferencia entre los dos es
que en un array podemos tener sólo un tipo de dato, al contrario del registro que nos permite definir cada
campo de una clase diferente; aunque cada campo debe tener un nombre único dentro del registro. Además,
para acceder a un campo de un registro necesitamos cualificar el campo, es decir indicar también a qué
registro pertenece. En cambio, para acceder a un miembro de un array sólo debemos indicar la posición del
mismo. Y la posibilidad de acceder a cada componente de un vector por un número es muy útil, para usarlo
en bucles.
También es posible definir matrices con el mismo procedimiento. Una matriz es como un arreglo de
vectores; un conjunto de números ordenados en una estructura bidimensional11 que se divide en filas y
columnas.
La definición del tipo es la misma que para un vector, pero agregamos la otra dimensión. Por ejemplo, para
definir una matriz de 4 filas y 3 columnas (12 números) hacemos:
11
Nota: los arrays en Pascal pueden tener más de dos dimensiones, simplemente definiendo todos
los rangos de índices; por ejemplo, una matriz de 2 x 5 x 4 se podría definir como
array[1..2,1..5,1..4]
Las estructuras de más de tres dimensiones no pueden ser representadas geométricamente. Hablaremos aquí
sólo de las matrices de dos dimensiones.
19
Object Pascal
type
tMatriz = array[0..3,0..2] of integer;
De esta manera, los elementos se referencian con el número de fila y de columna en que se encuentran12.
Como antes, para utilizar la estructura debemos declarar una variable de ese tipo:
var
a: tMatriz;
Sea v1=(2,5,3,89) y v2=(6,32,4,7) dos vectores de cuatro elementos. Se desea sumar estos dos vectores para
producir un tercero.
Los vectores se suman elemento a elemento; así, el primer elemento del vector resultante será la suma del
primer elemento de v1 y el primero de v2. Un programa Pascal que hace la suma y deja el resultado en el
vector vr es el siguiente:
type
tVector4 = array[1..4] of integer;
var
v1, v2, vr: tVector4;
i: integer;
begin
v1[1]:= 2; v2[1]:= 6;
v1[2]:= 5; v2[2]:= 32;
v1[3]:= 3; v2[3]:= 4;
v1[4]:= 89; v2[4]:= 7;
for i:= 1 to 4 do begin
vr[i]:= v1[i]+v2[i];
end;
end;
Type
tMatriz35 = array[1..3,1..5] of integer;
12
Nota: el hecho de comenzar la numeración en 0 no es obligatorio.
20
Ernesto Cullen
var
m1, m2, mr: tMatriz35;
i, j: integer;
begin
{suponemos que las matrices m1 y m2 ya tienen valores}
for i:= 1 to 3 do begin
for j:= 1 to 5 do begin
Mr[i,j]:= m1[i,j]+m2[i,j];
end;
end;
end;
Arreglos de registros
Los arrays no sólo pueden ser de números; podemos agrupar cualquier tipo de datos, incluso los definidos
por nosotros mismos. Por ejemplo, veamos una forma de almacenar los datos de 15 personas en memoria.
Definimos primero el registro que contendrá los datos de una persona (igual que antes), y luego el array de
registros:
type
tPersona = record
Nombre: string;
Apellido: string;
Direccion: string;
Telefono: string;
Sexo: char;
Edad: integer;
end;
var
Empleados: tPersArray;
Bien, tenemos en la variable Empleados lugar para colocar los datos de 15 personas. Podemos dar los datos
de la primera en el programa de la siguiente manera:
:
with Empleados[0] do begin
Nombre:= ‘Alguien’;
Apellido:= ‘Perez’;
Direccion:= ‘Pichincha 78';
Telefono:= ‘(032) 34523123
Sexo:= ‘M’;
Edad:= 56;
end;
:
Tipos enumerados
Un tipo enumerado se compone de un conjunto de valores. Una variable de este tipo sólo puede tomar uno
de los valores enumerados en la definición. Es una manera conveniente de restringir los valores que se
pueden asignar a una variable, para evitar errores.
21
Object Pascal
Ejemplos:
type
ColoresPrimarios = (Blanco,Negro,Rojo,Azul,Amarillo);
Nombres = (Juan,Pedro,Jose);
var
UnColorPrimario: ColoresPrimarios;
UnNombre, OtroNombre: Nombres;
Como vemos en los ejemplos, los identificadores que se utilizan en la declaración son constantes
cualesquiera, nombres arbitrarios para los distintos elementos posibles. Internamente, Delphi asigna un
número de orden correlativo a los elementos según su posición en la definición del tipo. Para el caso de
nuestro tipo ColoresPrimarios, el primer elemento (nro de orden 0) es Blanco, el segundo (nro. 1) es Negro,
y así sucesivamente.
Con estos tipos se pueden utilizar los operadores relacionales y los de asignación únicamente. Por ejemplo,
También podemos utilizarlos en sentencias de comparación, y por lo tanto como límites en bucles:
type
LongDeOnda = array[Blanco..Amarillo] of real;
var
a: LongDeOnda;
begin
a[Blanco]:= 342.56;
a[Rojo]:= 78.44;
Conjuntos (sets)
Otro identificador de tipo de datos especial de Pascal nos permite crear conjuntos de datos.
El tipo set referencia un conjunto de elementos. Estos elementos deben ser de un único tipo ordinal -que
tiene un orden de secuencia, esto implica por ejemplo la prohibición de usar reales- con una importante
restricción: el tipo elegido no puede tomar más de 256 valores diferentes. Por lo tanto también está vedado
el uso de integer o longint en la definición de conjuntos. Aún así, es uno de los tipos más usados de datos
en Delphi.
En la definición se utilizan las palabras claves set of y el tipo base de los elementos. Por ejemplo, un
conjunto de números primos se definiría de la siguiente manera:
type
22
Ernesto Cullen
var
ConjDePrimos: NrosPrimos;
begin
ConjDePrimos:= [3,5,7];
No obstante, nadie nos impide en el código anterior efectuar una asignación como ésta:
ConjDePrimos:= [3,5,6,78];
en la que podemos ver claramente que algunos elementos no son primos. La única restricción que impone la
definición del tipo es en el tipo de los elementos, no en sus valores. Para restringir los valores podemos usar
una combinación de tipos conjunto y enumerado:
type
Primos = (uno, dos, tres, cinco, siete);
NrosPrimos = set of Primos;
var
p,q: NrosPrimos;
begin
p:= [uno, tres, cinco]; {es correcto}
q:= [cuatro]; {es incorrecto}
Note sin embargo que aquí no podemos utilizar números, sólo constantes que los identifiquen.
El operador In
Para comprobar si un elemento existe en un conjunto, utilizamos el operador de relación In. La relación
<elemento> in <conjunto> devuelve TRUE si el elemento está presente en el conjunto, y FALSE si no.
cinco in p = TRUE
siete in p = FALSE aunque la sentencia es válida porque el valor está entre los valores posibles de p.
[1,2,3,4]+[5,6,3] = [1,2,3,4,5,6] Note que los elementos repetidos aparecen una sola vez.
(Unión de conjuntos)
23
Object Pascal
Los tipos enumerados (registros, enumerados y conjuntos) son muy utilizados en Pascal, y especialmente en
Delphi.
24