PROCEDIMIENTOS Y FUNCIONES CAPITULO 6
Bien... en el
último capítulo vimos en qué consistía el código de un programa. Hoy veremos
"otro tipo" de código: los procedimientos y las funciones. A este
"otro tipo" de código también se le suela llamar rutina, subrutina, a
veces se le llama módulo... y algún que otro nombre más que ahora se me debe olvidar.
Lo más común suele ser referirse genéricamente a procedimientos y funciones con
el término fución o rutina.
Un procedimiento o
una función es, por decirlo sencillamente, un trozo de código. Pero no
cualquier trozo de código. Sino un trozo que nosotros hemos separado por algún
motivo.
Pongamos un
ejemplo: estamos haciendo un programa que ha de calcular muchas factoriales.
Hemos hecho un código que calcula el factorial de un número, pero no queremos
tener que repetir todo ese código cada vez que haya que calcular un factorial.
Entonces lo que hacemos es separar ese código y darle un nombre, de manera que
después, con poner solamente el nombre ya no nos haga falta copiarlo todo.
También podría ser
que hiciésemos un programa que dibuja muchos cuadrados en la pantalla.
Igualmente
"aislaríamos" el trozo de código que dibuja el cuadrado para poder
usarlo más adelante.
Y, como no he
puesto dos ejemplos sólo por entretenerme, expliquemos en qué se diferencian
estos dos casos. En el primero, el del factorial, el código de la función tiene
que devolver un valor: el resultado de calcular el factorial, mientras que en
el segundo caso, para dibujar un cuadrado no nos hace falta que nos devuelva
ningún resultado; simplemente ha de dibujarlo y listos.
Por este motivo, el
primer caso sería una Función y el segundo sería un Procedimiento. La única diferencia
entre la función y el procedimiento es que la función devuelve un valor y el
procedimiento no.
DECLARACION
Ahora vemos cómo
nos las apañamos para separar el código en cuestión y que el Turbo Pascal se entere.
Para un
procedimiento usaríamos este esquema:
Procedure Nombre (
parámetros );
Y para una función
este otro:
Function Nombre (
parámetros ) : Tipo;
Donde
"Nombre" es el nombre que queremos darle a la función. Normalmente suele
corresponder a la función que hace, así, por ejemplo, la función que calcula el
factorial podría llamarse "Calcula_Factorial", aunque nada nos impide
llamarla "Inicia_Sound_Blaster".
Los parámetros nos
sirven para pasarle información a la función. El formato que hay que seguir es
igual al que usábamos para declarar las variables:
Procedure BlaBla (
Nombre1 : Tipo1; Nombre2 : Tipo2 );7
Podemos poner
cualquier número de parámetros y de cualquier tipo.
Podemos usar
enteros, cadenas, números en punto flotante, estructuras, punteros... cualquier
tipo de datos.
Y en las funciones,
por último, tenemos que indicar un "Tipo" ¿Qué tipo?
Pues el tipo de
datos del valor que devuelve la función. Esto es, como la función
Calcula_Factorial devolverá un número entero que puede ser bastante grande nos
convendrá poner que dicha función devuelve un dato de tipo LONGINT. La
declararíamos así:
Function Calcula_Factorial ( Numero : Longint ) :
Longint;
De esta manera,
pasaríamos como parámetro el número del cual hay que calcular el factorial y la
función nos devolvería el resultado en un longint.
VALOR Y
REFERENCIA
Ahora bien, no es
tan sencillo el paso de parámetros. Podemos pasarlos por valor o por
referencia.
Por valor es la
manera que hemos usado en los ejemplos anteriores. De esta manera lo que se
pasa a la función es, como si dijésemos, una "copia" de la variable
original. Con lo que, si modificamos el valor de ese parámetro no variará el
contenido de la variable original.
En cambio, al pasar
parámetros por referencia lo que se hace es pasar la variable original directamente.
(esto no es muy exacto que digamos, pero ya lo explicaré mejor cuando hayamos explicado
los punteros) Así, si modificamos el parámetro pasado por referencia TAMBIEN
modificamos la variable original. (al final pondré unos ejemplos para que quede
más claro)
Para que un
parámetro se pase por referencia tenemos que declararlo de una manera especial:
tenemos que preceder su nombre de la palabra reservada "var".
Pongamos un ejemplo:
Procedure
Calcula_Factorial (Numero : Longint; var Resultado : Longint);
En este ejemplo
pasamos "Numero" por valor y "Resultado" por referencia.
De esta manera
modificando "Numero" no modificamos la variable original que se ha
pasado como parámetro a la función, mientras que modificando "Resultado"
sí que modificamos el contenido original de la variable. De esta manera no
necesitaríamos declarar esta rutina como función. Podemos hacerlo como
procedimiento y devolver el resultado en "Resultado".
TIPOS, CONSTANTES
Y VARIABLES LOCALES
Después de la
declaración de la función podemos definir tipos de datos locales, constantes
locales y variables locales. Estos son como los tipos, constantes y variables
normales (que ya vimos) sólo que no podemos usarlos en otro lugar más que en la
función en la que han sido definidos. Es decir: desde una función podemos usar
los tipos/variables/constantes globales y también los locales que hayamos definido
en la función, pero desde el código principal (que explicamos el pasado capítulo)
sólo podemos acceder a las variables/constantes/tipos globales.
Y aquí nos puede
aparecer un conflicto: ¿qué pasa si tenemos una variable local que se llama
igual que una variable global? ¿A cual hace caso el Turbo Pascal? Pues siempre
que se dé este caso, cuando usemos el nombre de esas variables, el Turbo Pascal
asume que nos estamos refiriendo a la variable local y no a la global.
EL CODIGO DE LA
FUNCION
Una vez hemos
puesto todo lo anterior podemos poner un BEGIN...END; y escribir en medio el
trozo de código que queríamos separar del resto.
Atención a que en
este caso después del END no va un punto sino un punto y coma. (En el código
principal sí que va un punto).
Este código es
idéntico al código global sólo que tiene acceso a los tipos/constantes/variables globales y locales.
Desde una función podemos llamar a otra función o incluso a la misma función,
pero llamar a una misma función es ya otro tema; se le llama recursividad y ya
la explicaremos más adelante. Por ahora dejémoslo en que si una función se llama
a si misma lo que sucederá será que obtendremos un runtime error por
desbordamiento de pila. ¿Que significa esto? Pues sencillo: que la función se
llama a ella misma sin parar. Cada vez que se llama perdemos un poco de
memoria, que se usa para guardar las "copias" de los parámetros
pasados por valor y algunas otras cosas... si la rutina se llama a si misma
infinitamente llega un momento en que no queda memoria para pasar los
parámetros y por eso se produce el error.
En el caso de las
funciones (refiriéndonos ahora a las rutinas que devuelven un valor) tenemos
que devolver el valor antes de acabar el
código. Esto se
hace asignando un valor al nombre de la función. Es
decir: tratamos a
la función como si fuese una variable y le asignamos
un valor "a
saco". ;) Ejemplillo al canto:
Function Devuelve_5 : Longint;
Begin
Devuelve_5 := 5;
End;
Así de sencillo. :)
EJEMPLOS
Ahora que ya
sabemos toda la teoría sobre las funciones podemos hacer el ejemplo del
factorial. Pongamos que el código para hayar el factorial fuera este:
Factorial := 1;
For Contador := 1
To Numero Do
Resultado :=
Resultado * Contador;
Donde Factorial,
Contador y Numero son números enteros LONGINT.
Pues bien, o bien
copiamos ese trozo de código en todos los sitios donde necesitemos calcular el
factorial o bien lo aislamos en una función. La función podría quedar así:
Procedure Factorial ( Numero : Longint ) : Longint;
Var
Resultado, Contador
: Longint;
Begin
Resultado := 1;
For Contador := 1
to Numero Do
Resultado :=
Resultado * Contador;
Factorial :=
Resultado;
End;
Y después
llamaríamos a esta función desde el código principal:
Var
A : Longint;
Begin
A := Factorial( 8 );
A := Factorial( A
);
A := Factorial( A
div 10 );
End.
Como parámetro
podemos pasr cualquier expresión. Pero sólo en este caso.
Podemos pasar
cualquier expresión porque el parámetro está declarado como parámetro pasado
por valor. En el caso de parámetros por referencia no podemos usar un expresión
sino que tenemos que indicar una variable.
Por ejemplo,
podemos hacer que la función Factorial devuelva el resultado mediante un
parámetro pasado por referencia:
Procedure Factorial (Numero: Longint; var Resultado
: Longint);
Var
Contador : Longint;
Begin
Resultado := 1;
For Contador := 1
To Numero Do
Resultado :=
Resultado * Contador;
End;
Y esta función la
llamaríamos así desde el código principal:
Var
A : Longint;
Begin
Factorial ( 8, A );
End.
Pero algo de este
estilo provocaría un error de compilación:
Factorial ( 8, A +
5 );
Porque no podemos
pasar una expresión como parámetro por referencia. En cambio, ya que el primer
parámetro se pasa por valor, SI que podemos poner cosas que estas:
Factorial ( A + 8,
B );
Y más cosas... Un
detallito que parece no tener importancia: si os fijais vereis que al escribir
la "Function Factorial" el resultado se va calculando en una variable
local y después se asigna como resultado de la función. En cambio en el
"Procedure Factorial" el resultado se calcula directamente en el
parámetro por referencia en el que se ha de devolver el resultado.
¿Por qué? Pues
porque en la función, para ir calculando el valor directamente como resultado
de la función deberíamos poner una linea como esta:
Factorial :=
Factorial * Contador;
Y una estructura
del tipo := ... no es otra cosa que la manera de llamar a la función que se
indica. (tal y como hemos hecho en el código principal que llama a la función
Factorial). Por lo tanto, si pusiéramos esa linea en la función, el compilador
entendería que queremos llamar a la función Factorial dentro de la misma función
Factorial, con lo cual se produciría la "llamada infinita" de la que
he hablado antes y acabaría con un runtime error.
En cambio en el
procedimiento no corremos ese riesgo, ya que los parámetros se comportan
exactamente igual que si fuesen variables globales, con lo cual podemos usarlos
en cualquier sentencia de asignación normal.
Bueno... esto es
todo por hoy.
DESCARGAR CAPITULO 6 VERSIÓN PDF
CAPITULO 7
0 comentarios: