TRABAJO PRACTICO Nro 3

Anuncio
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
SUBPROGRAMAS EN PASCAL.
Llamaremos subprograma a un código compacto, contenido dentro de un programa,
y que realiza una tarea específica como validar datos de entrada, generar recuadros,
ordenar arreglos, realizar cálculos matemáticos, etc. Ud. sin saberlo ha estado utilizando estos subprogramas en forma totalmente transparente.
Cuando calculaba una raíz cuadrada mediante la expresión:
Raiz:=sqrt(x)
estuvo haciendo uso de la función sqrt( ) que en su esencia íntima no es otra cosa
que un código ejecutable, pero con la particularidad que el mismo ya fue escrito por
Borland en lugar de Ud. El valor encerrado entre paréntesis se denominaba un argumento o parámetro pasado a la función, y el prototipo de la misma (solicitado a través del help) indicaba las características de este parámetro y el valor devuelto por la
función:
function Sqrt( x : real): real;
Esto significaba que cualquier magnitud numérica asignada a x es convertida en un
real, realiza sus cálculos y retorna en el nombre en sí de la función, un resultado (el
valor de la raíz) de tipo real.
Un subprograma posee una estructura similar a la de un programa básico en Pascal:
Cabecera;
Bloque declarativo;
begin
instrucciones ejecutables;
end;
y este trozo de códigos debe ubicarse antes del (* main *) y en general de cualquier
invocación al mismo, entendiendo por invocación o llamado, al hecho en sí de utilizar la función. En el caso anterior:
Raiz:=sqrt(x)
se dice que hemos efectuado una invocación o llamada a la función sqrt( ).
Cabecera.
Comienza con la palabra clave function seguida a continuación de un nombre que
responda a las reglas ya vistas sobre sintaxis de identificadores. Si la función va
a trabajar sobre valores que le son pasados desde el punto de invocación (parámetros), entonces el nombre va acompañado de paréntesis dentro de los cuales se enumerarán los valores que recibirá, especificando su nombre y tipo.
Clase Teórica Nro 9
Pág. 1/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
El final de esta declarativa van dos puntos (:) y el tipo que retornará la función.
Veamos un ejemplo:
function ModuloZ( Re:double; Im:double):double;
La función se denomina ModuloZ (determina el módulo de un complejo Z cuyos
componentes real e imaginario se conocen) y recibe dos argumentos: Re e Im de tipo
double. El valor regresado por esta función será de tipo double.
Muy importante: Los nombres de estos parámetros son totalmente genéricos lo
cual significa que desde el punto de invocación la función puede ser llamada con
cualquier otro identificador, incluso valores escritos manualmente:
Modulo:=ModuloZ(3,4)
Tal vez desde otro lugar remoto del programa vuelva a requerir los servicios de esta
función sólo que con otros argumentos. Piense lo siguiente: Ud. dispone de 2 vectores
correspondientes a la parte real y a la parte imaginaria de una cierta cantidad de complejos y desea determinar sus magnitudes:
var
VectRe : array[1..DIM]of real;
VectIm : array[1..DIM]of real;
ModZ : array[1..DIM]of real;
i
: byte;
begin (* main *)
......................
for i:=1 to DIM do ModZ[i]:=ModuloZ(VectR[i],VectIm[i]);
end;
donde hemos invocado DIM veces a la función ModuloZ( ) pero con distintos argumentos. Sin embargo localmente siempre han sido recibidos con los mismos nombres Re e Im.
Estos parámetros en la cabecera de la función se denominan parámetros formales
y los que figuran en el punto de invocación se denominan parámetros locales.
Los parámetros formales se denominan así porque establecen la forma en que dichos
elementos serán recibidos, teniendo además el sentido de un verdadero bloque declarativo. Si la cantidad de estos parámetros es numerosa, puede recurrirse a la sintaxis
clásica de un bloque declarativo:
function Nombre ( Parámetro1 : tipo1;
Parámetro2 : tipo2;
..............................
Parámetron : tipo_n ):tipo_devuelto;
Estos argumentos pueden ser utilizados por el código operativo de la función como si
ellos hubiesen sido declarados en bloque propio de la función.
Clase Teórica Nro 9
Pág. 2/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
Muy importante: los parámetros locales (desde la invocación) deben corresponderse:
 En cantidad.
 En tipo
 En orden
con aquellos que son esperados en la declarativa formal del subprograma. Dicho en
otras palabras: si la function está esperando 3 parámetros, en la invocación o llamada a la función deben enviarse 3 parámetros. Si los dos primeros deben ser enteros
y el último de tipo double, deben corresponderse en ese orden.
A continuación de la cabecera viene el bloque de identificadores propios que requiera
el subprograma para su funcionamiento. Pueden incluir constantes y variables como
cualquier programa básico:
const .............
var .............
con tantos elementos como sean necesarios.
Todos estos identificadores: los parámetros formales y los del bloque declarativo, se
denominan identificadores locales y tienen vida mientras la función se halle activa (o sea operando).
Una vez que la función se ha extinguido porque su ejecución finalizó, todos estos
identificadores se pierden, y su explicación es muy sencilla:
Cada vez que una función es invocada, el compilador le asigna un espacio de memoria RAM para que la misma opere. En este espacio se almacenan tanto los parámetros formales como los identificadores del bloque declarativo, y de allí son tomados
por los códigos ejecutables para las operaciones de procesamiento. Una vez finalizada
la ejecución de la función esta zona de memoria es liberada perdiéndose todo lo que
había en ella.
Tanto los parámetros formales como los identificadores locales sólo pueden ser accesados por la propia función.
A continuación del bloque declarativo se escribirán los códigos ejecutables de la función que pueden ser tan complejos como se requiera. Ahora bien, para que la función
pueda retornar un valor en su propio nombre es necesario que en algún momento
(generalmente al final, aunque no es obligatorio) debe existir una instrucción en la cual
figure de izquierda a derecha:
NombreDeLaFunción:=Expresión;
En el ejemplo del módulo: ModuloZ:=sqrt(Re*Re+Im*Im);
Caso contrario la función no retornará nada.
Clase Teórica Nro 9
Pág. 3/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
IMPORTANTE:
Debe tomarse especial cuidado en no colocar el nombre de la función en el lado derecho, puesto que ello implicaría un nuevo llamado a la función en sí, creando
lo que se denomina recursividad. Este es un concepto muy potente que va más allá
del alcance de este curso.
Cuando estudiemos otro tipo de subprogramas denominados procedimientos, analizaremos dos modalidades adicionales muy importantes en la forma de recibir los parámetros formales, por ahora no complicaremos las cosas.
Veamos algunos ejemplos para afianzar todo lo dicho hasta ahora.
program Raiz_n;
{ ------------------------------------------------------------Implementar una función que determine cualquier orden de raíz
sobre cualquier base.
------------------------------------------------------------- }
uses crt;
var n
: double;
IndRaiz : double;
{ ------------------------------------------------------------ }
function Raiz(N:double; IndRaiz:double):double;
begin Raiz:=exp(ln(N)/IndRaiz); end;
{ ------------------------------------------------------------ }
begin (* main *)
clrscr; highvideo; n:=2; IndRaiz:=2;
writeln('Raiz cuadrada de 2 = ',Raiz(2,2):2:8);
readkey;
end.
Note la sencillez de esta función que está definida en una sola línea de códigos. Al tratarse de una sola línea es obvio que deberá contener el nombre de la función a fin de
que ésta pueda retornar algún valor.
Desde el punto de invocación (argumento de una instrucción writeln, la misma es llamada como cualquier otra función estándar propias de Pascal.
Observación importante.
El hecho de poder implementar tantas funciones como se nos ocurra, y de que su invocación posee la sintaxis clásica de cualquier comando Pascal, implica que lo que en
realidad estamos haciendo es crear nuevas instrucciones enriqueciendo las que
ya traía el lenguaje de por sí. Esto le da al lenguaje una potencialidad asombrosa.
He aquí más ejemplos:
Clase Teórica Nro 9
Pág. 4/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
program ComplejoDeBinario_Exponencial;
uses crt;
var
Re,Im : double;
Modulo : double;
Fi
: double;
{ ------------------------------------------------------------}
function ModuloZ(Re:double; Im:double):double;
begin ModuloZ:= sqrt(Re*Re+Im*Im); end;
{ ------------------------------------------------------------}
function AnguloZ(Re:double; Im:double):double;
const Kgrad = 180/PI;
var
Modu : double;
Seno : double;
begin
Modu:= sqrt(Re*Re+Im*Im);
if(Modu>0)then begin
Seno:= Im/Modu;
if(Seno>0)then
if(Re=0)then Fi:=90
else if(Re<0)then Fi:=180-Kgrad*arctan(abs(Im/Re))
else Fi:=Kgrad*arctan(abs(Im/Re))
else if(Re=0)then Fi:=270
else if(Re<0)then Fi:=180+Kgrad*arctan(abs(Im/Re))
else Fi:=270+Kgrad*arctan(abs(Im/Re));
end
else Fi:=0;
AnguloZ:=Fi;
end;
{ ------------------------------------------------------------}
begin (* main *)
clrscr; highvideo;
Re:=3; Im:=4;
Modulo:=ModuloZ(Re,Im);
Fi:=AnguloZ(Re,Im);
writeln('
writeln('
',Re:2:2,'+j',Im:2:2);
Mod=',Modulo:2:2,' Fi=',Fi:2:2,' grados');
readkey;
end.
Clase Teórica Nro 9
Pág. 5/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
Aquí hemos completado el pasaje de un complejo de la forma binaria a la forma exponencial o de Euler.
program CambioDeBaseLogaritmica;
{ --------------------------------------------------Desarrollar las siguientes funciones:
Log10(x) que determine el log decimal de x.
LogB(x) que determine el log de x en cualquier base.
---------------------------------------------------- }
uses crt;
var N : double;
{ --------------------------------------------------- }
function Log10(N:double):double;
begin Log10:=Ln(N)/Ln(10); end;
{ --------------------------------------------------- }
function LogB(N:double; B:double):double;
begin LogB:=Ln(N)/Ln(B); end;
{ --------------------------------------------------- }
begin (* main *)
clrscr; highvideo;
N:=14;
writeln('
writeln('
Log10(',N:2:2,')=',Log10(N):2:4);
Log10(',N:2:2,')=',LogB(N,10):2:4);
readkey;
end.
Este par de funciones es interesante puesto que amplía la capacidad de cálculo de
Pascal ya que permite determinar el logaritmo decimal y en general el logaritmo en
cualquier base, de un número “x” dado.
Otro tipo de subprograma: los procedimientos.
Los procedimientos, a diferencia de las funciones, no retornan ningún valor en su propio nombre, sino a través de sus parámetros. Su estructura es la siguiente:
procedure Nombre_del_procedimiento(lista de parámetros);
Bloque declarativo.
begin
instrucciones ejecutables;
end;
Clase Teórica Nro 9
Pág. 6/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
en vez de function va la palabra clave procedure. Luego todo es idéntico, excepto
que tampoco van los dos puntos verticales ni el tipo devuelto (porque no lo hay). ¿Entonces cómo puede devolver algún elemento que haya procesado? Bien, ha llegado el
momento de mejorar nuestro concepto acerca de los parámetros.
Parámetros por valor.
Son todos los vistos cuando estudiamos las funciones. Se denominan también parámetros de entrada, puesto que actúan como una copia de aquél que es enviado
desde el punto de invocación. Un ejemplo sencillo de la vida real aclarará este punto:
imagine que Ud. requiere de los servicios de impresión de un taller gráfico. Se acerca
al mostrador, pero en lugar de entregar el archivo original de su trabajo, le entrega una
copia del mismo. El empleado hace su tarea de impresión y luego tira a la basura el
CD, pero como se trataba de una copia, su original continúa a salvo. Esa es la idea.
Parámetros por referencia.
Aquí la cosa cambia, porque lo que se entrega en el mostrador no es una copia sino el
original. Esto significa que todo lo que el empleado haga sobre él permanecerá
cuando finalice con su trabajo. En la sintaxis el identificador va acompañado de la partícula var.
procedure CargarMatriz ( var M : TMat);
Aprovechamos y hacemos otro anuncio: en la declarativa de parámetros formales (ya
sea en procedimientos o en funciones) Pascal no permite declaraciones estructuradas, pero sí un tipo definido por el usuario en una línea type y que haga alusión a
dicha estructura:
const DIM1 = 5;
DIM2 = 7;
type TMat = array[1..DIM1, 1..DIM2]of integer;
La partícula var significa que al invocar al procedimiento CargarMatriz, el punto de invocación le está pasando la dirección donde reside el arreglo original, o sea que todo lo que el subprograma haga sobre el parámetro M, en realidad lo estará haciendo
sobre el original. Esto podemos querer hacerlo por dos razones:
 Porque nos interesa conservar las modificaciones realizadas.
 Porque el dato (un arreglo) es demasiado grande y resultaría costoso
en memoria pasarlo por valor y que se haga una copia local en el procedimiento.
Siempre un ejemplo es lo mejor para aclarar dudas:
Clase Teórica Nro 9
Pág. 7/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
program SubprogramaQueCargaMatriz;
{ -----------------------------------------------------------Desarrollar un procedimiento llamado CargarMatriz( ) cuya finalidad será cargar aleatoriamente una matriz de enteros de
DIM1 x DIM2, que recibirá como único parámetro. Los valores
aleatorios estarán comprendidos entre 50 y 100.
En el main declarar dos matrices: Mat1[ ] y Mat2[ ] que serán
cargadas por este procedimiento.
También en el main mostrar por pantalla, y en forma matricial,
el contenido asignado a ambos arreglos.
----------------------------------------------------------- }
uses crt;
const DIM1 = 5;
DIM2 = 7;
type TMat = array[1..DIM1,1..DIM2]of integer;
var
Mat1
Mat2
i,j
ColM1
FilM1
ColM2
FilM2
:
:
:
:
:
:
:
TMat;
TMat;
byte;
byte;
byte;
byte;
byte;
{ ------------------------------------------------------------ }
procedure CargarMatriz(var M : TMat);
var i,j : byte;
begin
for i:=1 to DIM1 do
for j:=1 to DIM2 do
M[i,j]:=50+random(51);
end;
{ ------------------------------------------------------------ }
begin (* main *)
clrscr; highvideo; randomize;
ColM1:=4; FilM1:=4;
ColM2:=40; FilM2:=4;
CargarMatriz(Mat1);
CargarMatriz(Mat2);
for i:=1 to DIM1 do begin
for j:=1 to DIM2 do begin
gotoxy(ColM1+(j-1)*4,FilM1); write(Mat1[i,j]);
gotoxy(ColM2+(j-1)*4,FilM2); write(Mat2[i,j]);
Clase Teórica Nro 9
Pág. 8/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
end;
FilM1:=FilM1+3;
FilM2:=FilM2+3;
end;
readkey;
end.
Para notar la diferencia entre un parámetro formal por referencia y por valor, haga la
siguiente prueba: modifique en la cabecera del procedimiento la declarativa de M eliminando la partícula var. Vea qué pasa cuando intenta mostrar por pantalla el arreglo
supuestamente cargado por el subprograma (que sabemos que al ser ahora un parámetro de entrada, los datos se pierden al finalizar su invocación).
Veamos otro ejemplo:
program MaxMinEnUnaMatriz;
{ -----------------------------------------------------Desarrollar dos procedimientos:
CargarMatriz( );
MatrizMaxMin( );
cuyas finalidades son:
La primera:
Cargar una matriz de enteros de DIM x DIM
elementos que será recibida como parámetro
por referencia. Al mismo tiempo irá mostrándola por pantalla en forma matricial.
La segunda:
Determinar el Máximo y el Mínimo elementos
almacenados.
Mostrar en pantalla el Máximo y el Mínimo devueltos por
el segundo procedimiento.
------------------------------------------------------ }
uses crt;
const DIM1 = 5;
DIM2 = 7;
type TMat = array[1..DIM1,1..DIM2]of integer;
var
Mat : TMat;
Max : integer;
Min : integer;
{ ----------------------------------------------------- }
Clase Teórica Nro 9
Pág. 9/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
procedure CargarMatriz(var M:TMat);
var i,j : byte;
begin
for i:=1 to DIM1 do begin
for j:=1 to DIM2 do begin
M[i,j]:=random(101);
write(M[i,j]:4);
end;
writeln;
end;
end;
{ ----------------------------------------------------- }
procedure MatrizMaxMin ( var M
: TMat;
var Max : integer;
var Min : integer );
var i,j : byte;
begin
Max:=-32768;
Min:=32767;
for i:=1 to DIM1 do
for j:=1 to DIM2 do begin
if(M[i,j]>Max)then Max:=M[i,j];
if(M[i,j]<Min)then Min:=M[i,j];
end;
end;
{ ----------------------------------------------------- }
begin (* main *)
clrscr; highvideo; randomize;
CargarMatriz(Mat);
MatrizMaxMin(Mat,Max,Min);
writeln;
writeln(' Max=',Max,'
Min=',Min);
readkey;
end.
Alcance de los identificadores.
Todo identificador (variable o constante) declarados en el bloque del main, puede
ser accesado desde cualquier ámbito del programa, ya sea desde los propios
códigos del programa principal o desde cualquier subprograma. Se dice entonces que
dicho identificador posee un alcance global, o más brevemente se trata de un identificador global.
Clase Teórica Nro 9
Pág. 10/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
En cambio los identificadores declarados dentro de un subprograma sólo pueden
ser accesados dentro del subprograma que le da vida, y en tanto el mismo se halle
activo. Se dice entonces que dichos identificadores son de alcance local o identificadores locales.
Modularidad.
Ya habrá captado que los subprogramas permiten aplicar la técnica de refinamientos
sucesivos al hacer posible la subdivisión de un programa complejo en muchos subprogramas de menor complejidad. He aquí las ventajas:
 Que muchos programadores puedan trabajar simultáneamente sobre distintos aspectos de un mismo problema.
 Ahorrar tiempo y esfuerzo al generar módulos que puedan ser llamados desde distintos puntos del programa.
 Permite reusabilidad de módulos que por su generalidad puedan ser utilizados por
distintos programas: sólo hay que incluirlos en su segmento de códigos.
 Permiten generar nuevas instrucciones enriqueciendo la potencia y alcance del lenguaje.
He aquí un ejemplo de este último punto:
program ArcoSeno;
{ ----------------------------------------------------------Implementar una función que permita determinar el ArcSen(x)
basándose en la Serie de Taylor:
ArcSen(x)=x+1/2*x3/3+1/2*3/4*x5/5+1/2*3/4*5/6*x7/7+....
calculado con no menos de 3000 términos.
---------------------------------------------------------- }
uses crt;
const Kgrad = 180/PI;
var
x
: double;
Arco : double;
{ ----------------------------------------------------------- }
function ArcSen(x:double):double;
var
i
: word;
n
: word;
Sum
: double;
{ acumulada de términos }
Pot_x
: double;
{ valor anterior de x
}
TermAct : double;
begin
Sum:=x; Pot_x:=x; i:=3;
Clase Teórica Nro 9
Pág. 11/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
repeat
n
:=i;
Pot_x :=Pot_x*x*x;
TermAct:=Pot_x/n;
repeat
n:=n-2;
TermAct:=TermAct*n/(n+1);
until(n=1);
Sum:=Sum+TermAct;
i :=i+2;
until(i>10000);
ArcSen:=Sum;
end;
{ ----------------------------------------------------------- }
begin (* main *)
clrscr; highvideo; Arco:=0.422618261; (* 25 grados *)
writeln('ArcSen(',Arco:1:9,')=',Kgrad*ArcSen(Arco):2:6);
readkey;
end.
program FuncionesLogicas;
{ ------------------------------------------------------Implementar una función lógica de tal suerte que reciba
como único parámetro un caracter y determine si se trata o no de una vocal.
------------------------------------------------------- }
uses crt;
{ ----------------------------------------------------------- }
function EsVocal(c:char):boolean;
begin
EsVocal:=false;
case c of
'a','e','i','o','u' : EsVocal:=true;
'A','E','I','O','U' : EsVocal:=true;
end;
end;
{ ----------------------------------------------------------- }
var Letra : char;
Texto : string;
i
: byte;
Voc
: byte; { contador de vocales en la cadena }
NoVoc : byte;
Clase Teórica Nro 9
Pág. 12/13
INFORMATICA – C.B.I. (Ciclo Básico de Ingeniería) - 2012
Departamento de Ciencias de la Computación – FACET / UNT
Grupo II – Dictado: Ing. Juan Manuel Conti.
begin (* main *)
clrscr; highvideo; Voc:=0; NoVoc:=0;
Texto:='Turbo Pascal es un lenguaje muy estructurado';
writeln(' ',Texto);
for i:=1 to length(Texto) do
if(EsVocal(Texto[i]))then Voc:=Voc+1
else NoVoc:=NoVoc+1;
writeln;
writeln('
writeln('
writeln('
writeln('
Longitud de la cad=',length(Texto));
Total de vocales =',Voc);
Total de otras
=',NoVoc);
Vocales+Novocales =',Voc+NoVoc);
readkey;
end.
program ParametrosPorValor;
{ ---------------------------------------------------Implementar un procedure llamado CargarValor( ) que
recibirá como parámetros, una variable "x" y un valor "k" que deberá asignárselo a x. Probar de recibir
"x" por valor y ver que se imprime en el main luego de
la invocación al procedimiento. Cambiar luego la modalidad a var y volver a verificar.
----------------------------------------------------- }
uses crt;
{ ---------------------------------------------------- }
procedure CargarValor(x:integer; n:integer);
begin x:=n; end;
{ ---------------------------------------------------- }
var a : integer;
k : integer;
begin (* main *)
clrscr; highvideo; a:=-12; k:=5;
CargarValor(a,k);
writeln(' a=',a);
readkey;
end.
Clase Teórica Nro 9
Pág. 13/13
Descargar