PROCEDIMIENTOS Y FUNCIONES CAPITULO 6

10:47:00 Unknown 0 Comments


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: