Programación en Pascal
Programación en Pascal
Programación en Pascal
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Programación</strong> <strong>en</strong> <strong>Pascal</strong><br />
(Geometría Computacional, 3 er curso de I.T. Topografía)<br />
Un programa <strong>en</strong> <strong>Pascal</strong><br />
El primer programa <strong>en</strong> <strong>Pascal</strong> que vamos a realizar es escribir <strong>en</strong> pantalla la frase "Hola<br />
Mundo". Veremos como pasar un simple algoritmo <strong>en</strong> seudocódigo a un programa <strong>en</strong> <strong>Pascal</strong>.<br />
algoritmo Dihola<br />
inicio<br />
ESCRIBIR "Hola Mundo"<br />
fin<br />
Tipos y estructuras de datos<br />
program Dihola;<br />
begin<br />
writeln ('Hola Mundo');<br />
<strong>en</strong>d.<br />
Un tipo de dato repres<strong>en</strong>ta un conjunto de valores. Si una variable, constante o expresión son<br />
de un tipo determinado, esto significa que sólo pued<strong>en</strong> tomar valores de ese conjunto.<br />
En <strong>Pascal</strong> exist<strong>en</strong> una serie de tipos básicos o primitivos proporcionados por el l<strong>en</strong>guaje:<br />
boolean, char, integer y real. El usuario también puede definir sus propios tipos <strong>en</strong> una sección<br />
TYPE.<br />
Declaraciones de tipo<br />
Sirv<strong>en</strong> para declarar nuevos tipos, dándoles un nombre. Ej:<br />
TYPE<br />
VAR<br />
TANIMAL = string [30];<br />
animal : TANIMAL;<br />
S<strong>en</strong>t<strong>en</strong>cias condicionales<br />
El flujo del programa no siempre es continuo sino que puede dep<strong>en</strong>der de ciertas condiciones,<br />
hablamos de las s<strong>en</strong>t<strong>en</strong>cias condicionales. En seudocódigo estas s<strong>en</strong>t<strong>en</strong>cias se escribían con<br />
SI-ENTONCES-SINO.<br />
Supongamos que queremos calcular el punto de intersección <strong>en</strong>tre dos rectas.<br />
La ecuación de la primera recta es: y=m1x+c1 y la segunda y=m2x+c2. Podemos resolver el<br />
problema mediante un simple sistema de dos ecuaciones con dos incognitas de la sigui<strong>en</strong>te<br />
manera:<br />
1
s<strong>en</strong>t<strong>en</strong>cia<br />
condicional<br />
{y=m1x+c1; -> x=(c1-c2)/(m2-m1)<br />
y=m2x+c2; -> y=m1x+c1<br />
}<br />
program Intersec_Rectas;<br />
var x,y,m1,m2,c1,c2:real;<br />
begin<br />
writeln ('Datos de la primera recta');<br />
write ('m1= '); readln(m1);<br />
write ('c1= '); readln(c1);<br />
writeln ('Datos de la segunda recta');<br />
write ('m2= '); readln(m2);<br />
write ('c2= '); readln(c2);<br />
lectura de<br />
datos<br />
if abs(m2-m1) < 0.00001 (*rectas paralelas*)<br />
th<strong>en</strong> writeln ('Rectas Paralelas, no intersectan')<br />
else begin<br />
x:=(c1-c2)/(m2-m1);<br />
y:=m1*x+c1;<br />
writeln ('Punto de intersección: (',x:4:2,',',y:4:2,')');<br />
<strong>en</strong>d;<br />
readln;<br />
<strong>en</strong>d.<br />
com<strong>en</strong>tarios<br />
declaración de<br />
variables<br />
cálculos y<br />
salida<br />
formateada<br />
Bucles<br />
En muchas ocasiones los programas repit<strong>en</strong> una serie de s<strong>en</strong>t<strong>en</strong>cias un número determinado o<br />
indeterminado de veces. Por ejemplo si deseamos leer por teclado los n vértices de un<br />
polígono, bastaría con escribir una s<strong>en</strong>t<strong>en</strong>cia de lectura g<strong>en</strong>érica, (por ejemplo que lea el<br />
vértice número i) y repetirla un total de n veces. Si recordamos, <strong>en</strong> seudocódigo exist<strong>en</strong> los<br />
bucles PARA-REPETIR, MIENTRAS y REPETIR-HASTA. Veremos sus modificaciones a<br />
<strong>Pascal</strong>.<br />
S<strong>en</strong>t<strong>en</strong>cia while (Mi<strong>en</strong>tras)<br />
• Sintaxis:<br />
while expresión_lógica do<br />
s<strong>en</strong>t<strong>en</strong>cia1;<br />
• s<strong>en</strong>t<strong>en</strong>cia1 puese ser una s<strong>en</strong>t<strong>en</strong>cia compuesta, <strong>en</strong> cuyo caso se escribiría:<br />
while expresión_lógica do<br />
begin<br />
conjunto se s<strong>en</strong>t<strong>en</strong>cias;<br />
<strong>en</strong>d;<br />
S<strong>en</strong>t<strong>en</strong>cia for (Para-Repetir)<br />
• Utilidad: escribir m<strong>en</strong>os <strong>en</strong> ciclos controlados por una variable contador.<br />
• Es m<strong>en</strong>os g<strong>en</strong>érica que while, exist<strong>en</strong> ciclos, que por su condición de iteración, pued<strong>en</strong><br />
expresarse mediante una s<strong>en</strong>t<strong>en</strong>cia while, pero no mediante una for.<br />
• Sintaxis:<br />
for vcb := valor_inicial to/downto valor_final do<br />
s<strong>en</strong>t<strong>en</strong>cia1;<br />
• Características:<br />
1) La variable de control del bucle (vcb) no debe cambiarse d<strong>en</strong>tro del bucle. Sus valores<br />
pued<strong>en</strong> utilizarse pero no deb<strong>en</strong> cambiarse. Esto es, la vcb puede aparecer <strong>en</strong> una<br />
expresión pero no debería aparecer <strong>en</strong> la parte izquierda de una s<strong>en</strong>t<strong>en</strong>cia de<br />
asignación.<br />
2
2) La vcb se increm<strong>en</strong>ta o decrem<strong>en</strong>ta automáticam<strong>en</strong>te <strong>en</strong> uno. Si necesita increm<strong>en</strong>tar o<br />
decrem<strong>en</strong>tar por otro valor debe usar while o repeat.<br />
3) Si s<strong>en</strong>t<strong>en</strong>cia1 es compuesta debe ir <strong>en</strong>cerrada <strong>en</strong>tre un par begin-<strong>en</strong>d.<br />
S<strong>en</strong>t<strong>en</strong>cia repeat (Repetir-Hasta)<br />
• Sintaxis:<br />
repeat<br />
s<strong>en</strong>t<strong>en</strong>cia1;<br />
until expresión_lógica;<br />
• s<strong>en</strong>t<strong>en</strong>cia1 puede ser compuesta, <strong>en</strong> cuyo caso se utiliza la misma sintaxis. Existe una<br />
difer<strong>en</strong>cia fundam<strong>en</strong>tal con la s<strong>en</strong>t<strong>en</strong>cia while. Ahora el bucle se ejecuta al m<strong>en</strong>os una vez<br />
porque la condición está ubicada al final del bucle, y nada impide que pase el flujo del<br />
programa la primera vez. Por otro lado, la condición de parada <strong>en</strong> un bucle suele ser justo la<br />
contraria <strong>en</strong> un caso que <strong>en</strong> otro. Es lógico, uno es mi<strong>en</strong>tras ocurra una cosa y el otro es hasta<br />
que esa cosa se cumpla.<br />
Por ejemplo supongamos un algoritmo que lea las coord<strong>en</strong>adas de tres puntos y los mueva<br />
tres puntos <strong>en</strong> la coord<strong>en</strong>ada x y escriba el resultado <strong>en</strong> algún dispositivo de salida:<br />
ALGORITMO lee_tres_vertices<br />
ENTRADA: las coord<strong>en</strong>adas (x,y) de tres puntos<br />
SALIDA: las coord<strong>en</strong>adas (x,y) de los tres puntos movidos 3 puntos hacia la<br />
derecha.<br />
VARIABLES: i:<strong>en</strong>tera<br />
x,y: real<br />
INICIO<br />
PARA i←1 HASTA 3 CON INCREMENTO +1<br />
ESCRIBE "Abscisa del punto número ", i<br />
LEER x<br />
ESCRIBE "Ord<strong>en</strong>ada del punto número ", i<br />
LEER Y<br />
ESCRIBE "El punto es (" x+3","y")"<br />
FIN_PARA<br />
FIN<br />
El programa equival<strong>en</strong>te a este algoritmo se muestra a continuación. Como podemos apreciar<br />
<strong>en</strong> un programa <strong>en</strong> <strong>Pascal</strong> es importantísimo no olvidar detalles de sintaxis. Por ejemplo cada<br />
s<strong>en</strong>t<strong>en</strong>cia termina <strong>en</strong> punto y coma. De cualquier forma es inmediato apreciar los simples<br />
cambios exist<strong>en</strong>tes.<br />
program lee_tres_vertices;<br />
var x,y:real;<br />
i:integer;<br />
begin<br />
for i:=1 to 3 do<br />
begin<br />
write ('Abscisa del punto número ',i); readln(x);<br />
write ('Ord<strong>en</strong>ada del punto número ',i); readln(y);<br />
writeln (' El punto es (',x+3,',',y,')');<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
Estructuras de datos: Arrays<br />
Declaración de un tipo array: TYPE id<strong>en</strong>tificador = array[valor_inicial..valor_final] of tipo;<br />
Declaración de una variable array:<br />
VAR id<strong>en</strong>tificador : array[valor_inicial .. valor_final] of tipo;<br />
3
VAR id<strong>en</strong>tificador : tipomatrizdefinida<strong>en</strong>TYPE;<br />
Si hay un único subrango t<strong>en</strong>emos un array unidim<strong>en</strong>sional o vector. Si exist<strong>en</strong> varios, un array<br />
multidim<strong>en</strong>sional.<br />
Un elem<strong>en</strong>to de un array puede ser utilizado <strong>en</strong> cualquier lugar donde se pueda poner una<br />
variable, la sintaxis para acceder es:<br />
id<strong>en</strong>tificador[expresión]<br />
las expresiones deb<strong>en</strong> coincidir con los valores permitidos del array correspondi<strong>en</strong>tes.<br />
Aunque un array debe ser pasado por valor <strong>en</strong> un subprograma siempre que no vaya a ser<br />
modificado, es preferible hacerlo por variable o refer<strong>en</strong>cia.<br />
Ejemplos de arrays:<br />
En nuestro caso hemos decidido definir tanto un punto como un polígono mediante arrays de la<br />
sigui<strong>en</strong>te forma:<br />
TYPE<br />
TipoPunto = array [0..1] of real;<br />
TipoPoligono = array [0..MAX_PUNTOS_POLIGONO-1] of TipoPunto;<br />
Pero recordar, esto sólo es la definición de los datos. Para poder utilizarlos se debe hacer<br />
mediante la declaración de una varialble:<br />
VAR<br />
a,b,c:TipoPunto;<br />
Ahora pued<strong>en</strong> operarse con a, b y c;<br />
AreaTriangulo2 := (a[0]*b[1]-a[1]*b[0] +<br />
b[0]*c[1]-b[1]*c[0] +<br />
c[0]*a[1]-c[1]*a[0]) ;<br />
Por supuesto también podemos hacer cosas como :<br />
a[0]:=3;<br />
b[1]:=5*a[1]-8;<br />
write (a[1]);<br />
readln( b[0]);<br />
{etcétera}<br />
La definición de TipoPolígono es algo más cojmpleja porque se trata de una matriz<br />
bidim<strong>en</strong>sional:<br />
x y<br />
Como ejemplo de uso, el sigui<strong>en</strong>te código lee una serie de puntos de teclado para formar un<br />
polígono:<br />
TYPE x=0; y=1;<br />
...<br />
for i:=0 to n-1 do<br />
begin<br />
write('P[',i,',x]= '); read(p[i,x]);<br />
write('P[',i,',y]='); readln(p[i,y]);<br />
4
<strong>en</strong>d;<br />
Procedimi<strong>en</strong>tos y funciones<br />
En el caso anterior hemos resuelto el problema realizando un programa. Sin embargo los<br />
programas reales ti<strong>en</strong>d<strong>en</strong> a ser bastante más grandes. Para que se facilite la tarea de<br />
programar sería adecuado dividir el problema <strong>en</strong> módulos, de modo que el programa no se<br />
construyera con innumerables líneas de código sino con una serie de llamadas a<br />
subalgoritmos, con las v<strong>en</strong>tajas de la mejora de la claridad <strong>en</strong> los programas, la reutilización de<br />
código y el mant<strong>en</strong>imi<strong>en</strong>to de los programas.<br />
Recordaremos que exist<strong>en</strong> dos tipos de subalgoritmos, procedimi<strong>en</strong>tos y funciones. Para<br />
decantarnos <strong>en</strong> utilizar un tipo u otro t<strong>en</strong>dremos <strong>en</strong> cu<strong>en</strong>ta la sigui<strong>en</strong>te tabla.<br />
Salida Simple Salida Estructurada<br />
1 Salida Función Procedimi<strong>en</strong>to<br />
Más de una salida Procedimi<strong>en</strong>to Procedimi<strong>en</strong>to<br />
Se considera un valor de salida a aquel que se obti<strong>en</strong>e d<strong>en</strong>tro del subalgoritmo a partir de otros<br />
de <strong>en</strong>trada. El tipo de dato de salida se considera simple cuando es un tipo definido por el<br />
propio l<strong>en</strong>guaje, como los <strong>en</strong>teros, reales, lógicos, etc. Los tipos estructurados los suele crear<br />
el usuario a partir de otros tipos de datos simples, por ejemplo un polígono, una lista de notas<br />
de alumnos, etc. Exist<strong>en</strong> otros datos definidos por el usuario que sin embargo pued<strong>en</strong><br />
considerarse simples, puestos que están compuestos de un número muy pequeño de<br />
elem<strong>en</strong>tos, un punto, un número complejo, etc. Sin embargo nosotros lo consideraremos<br />
estructurados para simplificar.<br />
Por ejemplo la suma de tres valores <strong>en</strong>teros es una función porque indep<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te del<br />
número de elem<strong>en</strong>tos que <strong>en</strong>tran sólo sale un valor y éste es simple<br />
a<br />
b<br />
c<br />
suma<br />
Sin embargo un subalgoritmo que intercambie los cont<strong>en</strong>idos de dos variables o que devuelva<br />
un polígono, será un procedimi<strong>en</strong>to. En el primer caso porque sal<strong>en</strong> dos variables y <strong>en</strong><br />
segundo porque el tipo de dato de salida es estructurado.<br />
a<br />
b<br />
Recordaremos brevem<strong>en</strong>te los procedimi<strong>en</strong>tos y funciones <strong>en</strong> seudocódigo y sus equival<strong>en</strong>cias<br />
<strong>en</strong> <strong>Pascal</strong>.<br />
FUNCION Suma (a,b,c:<strong>en</strong>tero):<strong>en</strong>tero<br />
ENTRADA; a,b,c<br />
SALIDAD: a+b+c<br />
VARIABLES:<br />
INICIO<br />
Suma ← a+b+c<br />
FIN<br />
b<br />
a<br />
FUNCTION Suma (a,b,c:integer):interger;<br />
BEGIN<br />
Suma := a+b+c;<br />
END;<br />
5
PROCEDIMIENTO Intercambio(var a,b:<strong>en</strong>tero)<br />
ENTRADA: a,b<br />
SALIDA: a,b<br />
VARIABLES: aux<br />
INICIO<br />
aux ← a<br />
a ← b<br />
b ← aux<br />
FIN<br />
PROCEDURE Intercambia(var a,b:integer);<br />
VAR: aux<br />
BEGIN<br />
aux := a;<br />
a := b;<br />
b := aux;<br />
END;<br />
Como podemos observar el paso de seudocódigo a pascal es inmediato, sólo es necesario<br />
recordar las reglas de sintáxis y algunos aspectos básicos.<br />
• Los subalgoritmos terminan con (;) y no con (.)<br />
• Los parámetros pasados por refer<strong>en</strong>cia se distingu<strong>en</strong> por llevar igualem<strong>en</strong>te la partícula<br />
var. Se pasan por valor <strong>en</strong> caso contrario.<br />
• Nunca se realizan operaciones de <strong>en</strong>trada/salida <strong>en</strong> un procedimi<strong>en</strong>to o función, toda la<br />
información <strong>en</strong>tra y sale como parámetro.<br />
La pregunta ahora es determinar cuando un parámetro es pasado por valor o por refer<strong>en</strong>cia.<br />
Para ello nos ayudará la sigui<strong>en</strong>te tabla:<br />
Para cada parámetro tipo de dato tipo de dato<br />
simple<br />
estructurado<br />
de <strong>en</strong>trada por valor por refer<strong>en</strong>cia<br />
de salida o de<br />
<strong>en</strong>trada/salida<br />
por refer<strong>en</strong>cia por refer<strong>en</strong>cia<br />
Esto nos ayuda a saber porqué los parámetros a,b y c de la función está pasados por valor<br />
(son de <strong>en</strong>trada y simples), mi<strong>en</strong>tras que los parámetros a y b del procedimi<strong>en</strong>to son pasados<br />
por refer<strong>en</strong>cia (son de <strong>en</strong>trada y salida al mismo tiempo).<br />
Ahora estamos <strong>en</strong> disposición de modificar el programa que calcula el punto de intersección<br />
<strong>en</strong>tre dos rectas. Sería muy conv<strong>en</strong>i<strong>en</strong>te mant<strong>en</strong>er un subalgoritmo con esta operación y<br />
simplem<strong>en</strong>te hacer una llamada siempre que nos interese, de este modo, basta con escribirlo<br />
una vez y utilizarlo siempre que fuera necesario.<br />
At<strong>en</strong>di<strong>en</strong>do al programa, vemos que:<br />
• El procedimi<strong>en</strong>to queda después de la definición del programa, las constantes y variables y<br />
antes del programa principal.<br />
• Se consideran com<strong>en</strong>tarios lo que quede <strong>en</strong>tre llaves o <strong>en</strong>tre los símbolos (*...*)<br />
• La <strong>en</strong>trada/salida se realiza <strong>en</strong> el programa principal, no <strong>en</strong> el procedimi<strong>en</strong>to, a no ser que<br />
sea un procedimi<strong>en</strong>to exclusivo de <strong>en</strong>trada o salidada.<br />
• El procedimi<strong>en</strong>to se invoca mediante una llamada. Los parámetros se correspond<strong>en</strong> por<br />
posición, no por nombre. Así p<strong>en</strong>di<strong>en</strong>te1 se correspond<strong>en</strong> con el parámetro m1, const1 con<br />
c1, y así sucesivam<strong>en</strong>te.<br />
• Los parámetros x e y (coincid<strong>en</strong> <strong>en</strong> nombre por casualidad) son pasados por refer<strong>en</strong>cia<br />
porque son de salida.<br />
6
program interseccion;<br />
const infinito=9.9e9;<br />
var p<strong>en</strong>di<strong>en</strong>te1, p<strong>en</strong>di<strong>en</strong>te2, const1, const2,x,y:real;<br />
procedure Intersec_Rectas (m1,c1,m2,c2:real; VAR x,y:real);<br />
begin<br />
if abs(m2-m1) < 0.00001 (*p<strong>en</strong>di<strong>en</strong>te infinita*)<br />
th<strong>en</strong> begin<br />
x:=infinito; y:=infinito;<br />
<strong>en</strong>d<br />
else begin<br />
x:=(c1-c2)/(m2-m1);<br />
y:=m1*x+c1;<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
{ -------------------PROGRAMA PRINCIPAL------------------}<br />
begin<br />
writeln ('Datos de la primera recta');<br />
write ('p<strong>en</strong>di<strong>en</strong>te1= '); readln(p<strong>en</strong>di<strong>en</strong>te1);<br />
write ('const1= '); readln(const1);<br />
writeln ('Datos de la segunda recta');<br />
write ('p<strong>en</strong>di<strong>en</strong>te2= '); readln(p<strong>en</strong>di<strong>en</strong>te2);<br />
write ('const2= '); readln(const2);<br />
Intersec_Rectas(p<strong>en</strong>di<strong>en</strong>te1,const1,p<strong>en</strong>di<strong>en</strong>te2,const2, x,y);<br />
if x=infinito<br />
th<strong>en</strong> writeln ('rectas paralelas')<br />
else writeln ('intersección <strong>en</strong> (',x:4:2,',',y:4:2,')');<br />
readln;<br />
<strong>en</strong>d.<br />
Com<strong>en</strong>tarios:<br />
Como no puede tratarse computacionalm<strong>en</strong>te el valor infinito, supongamos que tomamos un<br />
valor constante cualquiera sufici<strong>en</strong>tem<strong>en</strong>te alto. De esta forma trataremos el problema.<br />
Ejemplo: Intersección de dos segm<strong>en</strong>tos<br />
Si quisieramos conocer si intersectan o no dos segm<strong>en</strong>tos definidos por los sus puntos<br />
extremos, t<strong>en</strong>dremos que realizar los pasos sigui<strong>en</strong>tes:<br />
1. conocer las ecuaciones de la recta que pasan que pasan por ambos segm<strong>en</strong>tos<br />
2. calcular el punto de intersección <strong>en</strong>tre las dos rectas que conti<strong>en</strong>e a ambos segm<strong>en</strong>tos<br />
3. ver si el punto de intersección de ambas rectas queda d<strong>en</strong>tro de los segm<strong>en</strong>tos.<br />
Como vemos, el paso número 2 ya está implem<strong>en</strong>tado con el procedimi<strong>en</strong>to Intersec_Rectas,<br />
una de las v<strong>en</strong>tajas que ti<strong>en</strong>e la programación modular. También sería interesante realizar un<br />
subalgoritmo que dado un segm<strong>en</strong>to construya devuelva la ecuación de la recta que lo<br />
conti<strong>en</strong>e. Una vez construido dicho procedimi<strong>en</strong>to se llamará dos veces, una para cada<br />
segm<strong>en</strong>to implicado.<br />
Como hemos visto <strong>en</strong> teoría, es siempre más recom<strong>en</strong>dable utilizar la aritmética de <strong>en</strong>teros<br />
que la de reales, sin embargo, si lo que deseamos es conocer el punto de intersección de dos<br />
rectas o segm<strong>en</strong>tos no hay más remedio que emplear esta última.<br />
program Interseccion_Segm<strong>en</strong>tos;<br />
CONST x=0; y=1;<br />
infinito=9.9e9;<br />
TYPE<br />
TipoPunto = array [0..1] of real;<br />
var<br />
a,b,c,d:TipoPunto;<br />
xx,yy:real;<br />
7
{--------------------------------------------------------------------}<br />
{(x,y) ser el punto de intersecci¢n de las rectas (m1,c1) y (m2,c2)<br />
si son paralelas x e y son igual a infinito}<br />
procedure Intersec_Rectas (m1,c1,m2,c2:real; VAR x,y:real);<br />
begin<br />
if (m1=infinito) and (m2=infinito) {rectas verticales paralelas}<br />
th<strong>en</strong> begin<br />
x:=infinito; y:=infinito;<br />
<strong>en</strong>d<br />
else if m1=infinito {la ecuación de la recta es x=c1}<br />
th<strong>en</strong> begin<br />
x:=c1; y:=m2*x+c2;<br />
<strong>en</strong>d<br />
else if m2=infinito<br />
th<strong>en</strong> begin<br />
x:=c2; y:=m1*x+c1;<br />
<strong>en</strong>d<br />
else if abs(m2-m1) < 0.00001 (*son rectas paralelas*)<br />
th<strong>en</strong> begin<br />
x:=infinito; y:=infinito;<br />
<strong>en</strong>d<br />
else begin<br />
x:=(c1-c2)/(m2-m1);<br />
y:=m1*x+c1;<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
{-------------------------------------------------------------------------}<br />
{devuelve la ecuación de la recta (m,c) que forma un segm<strong>en</strong>to dado por los<br />
puntos (p1,p2); si la recta resultante es vertical, la p<strong>en</strong>di<strong>en</strong>te m=infinito}<br />
procedure Segm<strong>en</strong>toARecta (var p1,p2:TipoPunto; var m,c:real);<br />
begin<br />
if abs(p2[x]-p1[x]) < 0.00001 (*p<strong>en</strong>di<strong>en</strong>te infinita *)<br />
th<strong>en</strong> begin<br />
m:=infinito; {la ecuación de la recta es x=x1, hacemos x=c1}<br />
c:=p1[x];<br />
<strong>en</strong>d<br />
else begin<br />
m:=(p2[y]-p1[y])/(p2[x]-p1[x]); {m=(y2-y1)/(x2-x1)}<br />
c:=p1[y]-m*p1[x]; {c=y1-mx1}<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
{--------------------------------------------------------------------}<br />
{indica el punto <strong>en</strong> que intersectan dos segm<strong>en</strong>tos, <strong>en</strong> el caso de no hacerlo<br />
xx e yy son igual a infinito}<br />
procedure Intersec_Segm<strong>en</strong>tos (var a,b,c,d:TipoPunto; var xx,yy:Real);<br />
var<br />
m1,m2,c1,c2:real;<br />
begin<br />
Segm<strong>en</strong>toARecta(a,b,m1,c1);<br />
Segm<strong>en</strong>toARecta(c,d,m2,c2);<br />
Intersec_Rectas (m1,c1,m2,c2,xx,yy);<br />
{la x está <strong>en</strong> el rango del segm<strong>en</strong>to (a,b)}<br />
if not (((a[x]
writeln ('Datos del primer segm<strong>en</strong>to');<br />
writeln ('Datos del primer punto del segm<strong>en</strong>to');<br />
write ('x1= '); readln(a[x]);<br />
write ('y1= '); readln(a[y]);<br />
writeln ('Datos del segundo punto del segm<strong>en</strong>to');<br />
write ('x2= '); readln(b[x]);<br />
write ('y2= '); readln(b[y]);<br />
writeln ('Datos del segundo segm<strong>en</strong>to');<br />
writeln ('Datos del primer punto del segm<strong>en</strong>to');<br />
write ('x1= '); readln(c[x]);<br />
write ('y1= '); readln(c[y]);<br />
writeln ('Datos del segundo punto del segm<strong>en</strong>to');<br />
write ('x2= '); readln(d[x]);<br />
write ('y2= '); readln(d[y]);<br />
Intersec_Segm<strong>en</strong>tos (a,b,c,d,xx,yy);<br />
writeln ('intersecci¢n <strong>en</strong> el punto (',xx:4:2,',',yy:4:2,')');<br />
readln;<br />
<strong>en</strong>d.<br />
Registros<br />
Los registros permit<strong>en</strong> mant<strong>en</strong>er información de distinto tipo bajo el mismo concepto. Por<br />
ejemplo, el registro alumno t<strong>en</strong>dría el nombre y apellidos, el DNI, las asignaturas cursadas <strong>en</strong><br />
el curso actual, la calificaciones, etc. Todos estos datos son de distinta naturaleza (<strong>en</strong>teros,<br />
cad<strong>en</strong>as de caracteres, etc), sin embargo, pose<strong>en</strong> la característica de pert<strong>en</strong>ecer a la <strong>en</strong>tidad<br />
alumno. Por tanto, ti<strong>en</strong>e s<strong>en</strong>tido almac<strong>en</strong>arse bajo un mismo nombre alumno, y eso lo<br />
conseguiremos defini<strong>en</strong>do un registro. Cada uno de los elem<strong>en</strong>tos definidos d<strong>en</strong>tro de un<br />
registro se llaman campos. Veamos de forma g<strong>en</strong>érica la definición.<br />
TYPE nombre_tipo = RECORD<br />
id<strong>en</strong>tificadorCampo1 : tipo1;<br />
...<br />
id<strong>en</strong>tificadorCampoN : tipoN;<br />
END;<br />
Id<strong>en</strong>tificador de campo: es el nombre de una compon<strong>en</strong>te de un registro.<br />
Por ejemplo, un punto, una recta, un número complejo, etc. son tipos de datos estructurados<br />
porque están compuestos a su vez de otros tipos de datos. Ya hemos visto que<br />
computacionalm<strong>en</strong>te hablando, es mejor definir un punto mediante un array de dos posiciones.<br />
Esto es posible hacerlo porque sus dos campos son del mismo tipo. Vamos a definir para<br />
variar, el tipo de dato TipoRecta como un registro con dos campos, la p<strong>en</strong>di<strong>en</strong>te y la constante<br />
según indica la fórmula y=mx+c.<br />
type<br />
TipoRecta = RECORD<br />
m,c:real;<br />
<strong>en</strong>d;<br />
Cuando deseemos declarar una variable de tipo TipoRecta, lo haremos de la sigui<strong>en</strong>te forma:<br />
var<br />
r:TipoRecta;<br />
Para acceder a cada uno de los campos de un registro utilizaremos:<br />
. . En el ejemplo anterior:<br />
y=r.m*x+r.c<br />
9
ya que tanto m como c son campos del registro r.<br />
Las unidades <strong>en</strong> <strong>Pascal</strong> (UNIT)<br />
Una unidad <strong>en</strong> <strong>Pascal</strong> no es más que una librería de funciones y procedimi<strong>en</strong>tos que el usuario<br />
ti<strong>en</strong>e definidos y que utiliza <strong>en</strong> cada programa cuando lo necesite. Por ejemplo, p<strong>en</strong>semos <strong>en</strong><br />
una librería de funciones matemáticas sobre manejo de matrices. Una vez que la operación de<br />
suma de matrices esté definida y funcione perfectam<strong>en</strong>te, bastará con incluir la unidad donde<br />
está escrita y hacer la llamada a la función como si estuviera ubicada <strong>en</strong> el mismo fichero del<br />
programa principal.<br />
Una unidad se d<strong>en</strong>omina unit y posee dos partes. La d<strong>en</strong>ominada INTERFACE donde se<br />
defin<strong>en</strong> los tipos de datos, las constantes necesarias y las cabeceras de los procedimi<strong>en</strong>tos y<br />
funciones. Posteriorm<strong>en</strong>te aparece la palabra reservada IMPLEMENTATION, a partir de donde<br />
se escrib<strong>en</strong> todas las funciones y procedimi<strong>en</strong>tos completos cuyas cabeceras están <strong>en</strong> la zona<br />
interface.<br />
Vamos a escribir una unidad completa con una serie de procedimi<strong>en</strong>tos y funciones válidos <strong>en</strong><br />
nuestra asignatura de geometría computacional. Posee las funciones ya previam<strong>en</strong>te vistas, la<br />
de intersección de dos rectas y dos segm<strong>en</strong>tos. Pero además aparec<strong>en</strong> otras que pued<strong>en</strong> ser<br />
igualem<strong>en</strong>te útiles:<br />
UNIT GEOMLIB;<br />
INTERFACE<br />
CONST x=0; y=1;<br />
infinito=9.9e9;<br />
MAX_PUNTOS_POLIGONO = 100;<br />
TYPE<br />
TipoPunto = array [0..1] of real;<br />
TipoPoligono = array [0..MAX_PUNTOS_POLIGONO-1] of TipoPunto;<br />
TipoRecta = RECORD<br />
m,c:real;<br />
<strong>en</strong>d;<br />
TipoSegm<strong>en</strong>to = array [0..1] of TipoPunto;<br />
procedure Intersec_Rectas (r1,r2:TipoRecta; VAR x,y:real);<br />
procedure Segm<strong>en</strong>toARecta (var p1,p2:TipoPunto; var r:TipoRecta);<br />
procedure Intersec_Segm<strong>en</strong>tos (var a,b,c,d:TipoPunto; var xx,yy:Real);<br />
function AreaPoligono2 (var p:TipoPoligono; n:integer): real;<br />
function AreaTriangulo2 (var a,b,c:TipoPunto) : real;<br />
procedure LeerPoligonoTeclado (var p:TipoPoligono; var n:integer);<br />
procedure LeerPoligonoFichero (var f:string; var p:TipoPoligono; var<br />
n:integer);<br />
procedure EscribirPoligonoFichero (var f:string; var p:TipoPoligono;<br />
n:integer);<br />
procedure EscribirPoligonoPantalla (var p:TipoPoligono; n:integer);<br />
IMPLEMENTATION<br />
{(x,y) ser el punto de intersección de las rectas (m1,c1) y (m2,c2)<br />
si son paralelas x e y son igual a infinito}<br />
procedure Intersec_Rectas (r1,r2:TipoRecta; VAR x,y:real);<br />
begin<br />
if (r1.m=infinito) and (r2.m=infinito) {rectas verticales paralelas}<br />
th<strong>en</strong> begin<br />
x:=infinito; y:=infinito;<br />
<strong>en</strong>d<br />
else if r1.m=infinito {la ecuación de la recta es x=c1}<br />
th<strong>en</strong> begin<br />
x:=r1.c; y:=r2.m*x+r2.c;<br />
<strong>en</strong>d<br />
else if r2.m=infinito<br />
10
th<strong>en</strong> begin<br />
x:=r2.c; y:=r1.m*x+r1.c;<br />
<strong>en</strong>d<br />
else if abs(r2.m-r1.m) < 0.00001 (*son rectas paralelas,<br />
intersectan <strong>en</strong> el infinito*)<br />
th<strong>en</strong> begin<br />
x:=infinito; y:=infinito;<br />
<strong>en</strong>d<br />
else begin<br />
x:=(r1.c-r2.c)/(r2.m-r1.m);<br />
y:=r1.m*x+r1.c;<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
{devuelve la ecuación de la recta (m,c) que forma un segm<strong>en</strong>to dado por los<br />
puntos (p1,p2); si la recta resultante es vertical, la p<strong>en</strong>di<strong>en</strong>te m=infinito}<br />
procedure Segm<strong>en</strong>toARecta (var p1,p2:TipoPunto; var r:TipoRecta);<br />
begin<br />
if abs(p2[x]-p1[x]) < 0.00001 (*son rectas paralelas, intersectan <strong>en</strong> el<br />
infinito*)<br />
th<strong>en</strong> begin<br />
r.m:=infinito; {la ecuación de la recta es x=x1, pero lo dejamos <strong>en</strong> c}<br />
r.c:=p1[x];<br />
<strong>en</strong>d<br />
else begin<br />
r.m:=(p2[y]-p1[y])/(p2[x]-p1[x]); {m=(y2-y1)/(x2-x1)}<br />
r.c:=p1[y]-r.m*p1[x]; {c=y1-mx1}<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
procedure Intersec_Segm<strong>en</strong>tos (var a,b,c,d:TipoPunto; var xx,yy:Real);<br />
var<br />
r1, r2: TipoRecta;<br />
begin<br />
Segm<strong>en</strong>toARecta(a,b,r1);<br />
Segm<strong>en</strong>toARecta(c,d,r2);<br />
Intersec_Rectas (r1,r2,xx,yy);<br />
if not (((a[x]
AreaPoligono2 := suma;<br />
<strong>en</strong>d;<br />
procedure LeerPoligonoTeclado (var p:TipoPoligono; var n:integer);<br />
var i:integer;<br />
begin<br />
write ('Número de vértices del polígono: '); readln (n);<br />
for i:=0 to n-1 do<br />
begin<br />
write('P[',i,',x]= '); read(p[i,x]);<br />
write('P[',i,',y]='); readln(p[i,y]);<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
procedure EscribirPoligonoPantalla (var p:TipoPoligono; n:integer);<br />
var i:integer;<br />
begin<br />
writeln ('Escribiremos el polígono de ',n,' v‚rtices');<br />
for i:=0 to n-1 do<br />
begin<br />
write('P[',i,',x]= ',p[i,x]:4:2); write(' '); writeln('P[',i,',y]=<br />
',p[i,y]:4:2);<br />
<strong>en</strong>d;<br />
<strong>en</strong>d;<br />
procedure LeerPoligonoFichero (var f:string; var p:TipoPoligono; var<br />
n:integer);<br />
var i:integer;<br />
fich: TEXT;<br />
begin<br />
assign (fich, f);<br />
reset (fich);<br />
readln (fich,n);<br />
for i:=0 to n-1 do<br />
read(fich,p[i,x]); readln(fich,p[i,y]);<br />
close (fich);<br />
<strong>en</strong>d;<br />
procedure EscribirPoligonoFichero (var f:string; var p:TipoPoligono;<br />
n:integer);<br />
var i:integer;<br />
fich: TEXT;<br />
begin<br />
assign (fich, f);<br />
rewrite (fich);<br />
writeln (fich,n);<br />
for i:=0 to n-1 do<br />
write(fich,p[i,x]:4:2); write(fich,' ');writeln(fich,p[i,y]:4:2);<br />
close (fich);<br />
<strong>en</strong>d;<br />
<strong>en</strong>d.<br />
FICHEROS<br />
Para terminar aclararemos dos de los procedimi<strong>en</strong>tos anteriorm<strong>en</strong>te implem<strong>en</strong>tados <strong>en</strong> la<br />
unidad geomlib.pas, se trata de los procedimi<strong>en</strong>tos LeerPoligonoFichero y<br />
EscribirPoligonoFichero que utilizan ficheros.<br />
Si nos fijamos son muy similares a los de leer de teclado y escribir <strong>en</strong> pantalla, sin embargo, la<br />
<strong>en</strong>trada y salida será ahora a través de fichero. Esto significa que los datos de <strong>en</strong>trada, los<br />
puntos, estarán dispuestos <strong>en</strong> un fichero de texto (lo escribe el usuario con cualquier editor de<br />
textos); la salida, los resultados, también serán llevados a un fichero.<br />
12
Cuando el fichero sirve de <strong>en</strong>trada al programa, los datos deb<strong>en</strong> estar <strong>en</strong> el fichero antes de la<br />
ejecución del programa. Cuando el fichero es de salida, éste comi<strong>en</strong>za estando vacío y termina<br />
con los resultados del programa.<br />
En nuestro ejemplo, el fichero de <strong>en</strong>trada y de salida posee la sigui<strong>en</strong>te estructura:<br />
n<br />
x0 y0<br />
x2 y2<br />
x3 y3<br />
....<br />
xn-1 yn-1<br />
Por ejemplo para un polígono de 4 vértices:<br />
4<br />
0 0<br />
5 1<br />
3 4<br />
-3 2<br />
Todo fichero debe t<strong>en</strong>er un nombre datos.dat, poligono.dat, valores.txt, o cualquier otro que el<br />
usuario decida. El nombre del fichero será <strong>en</strong> estos procedimi<strong>en</strong>tos el primer parámetro, la<br />
cad<strong>en</strong>a de caracteres o string. La operación assign asigna un nombre de fichero a una variable<br />
fichero. Luego se puede abrir el fichero para leer (reset) o para escribir <strong>en</strong> él (rewrite). El resto<br />
de operaciones, leer y escribir, son idénticas a las correspondi<strong>en</strong>tes para pantalla y teclado, sin<br />
embargo, observamos que el primer parámetro es el nombre de la variable fichero. Hay que<br />
t<strong>en</strong>er especial cuidado con el formato del fichero. Para que no haya problemas utilizaremos la<br />
terminación ln <strong>en</strong> las operaciones de write y read siempre que después haya que realizar un<br />
salto de línea.<br />
13