Está en la página 1de 16

Procedimientos, Funciones y Recursividad

Gonzalo Soriano
gsorianob@gmail.com
7 de abril de 2009

”Divide y vencerás”
1. Introducción a procedimientos y funciones.
Un procedimiento o función es un bloque de código que no se encuentra en
el bloque o programa principal; pero puede ser invocado por éste cuantas veces
quiera el programador.
Cuando tenemos que resolver un gran programa, muchas veces es muy útil di-
vidirlo en varios pequeños problemas; de esta forma resolvemos más problemas,
pero más sencillos.
También es muy útil usar procedimientos y funciones cuando una tarea la ten-
emos que realizar varias veces; de esta forma, la implementamos una vez y la
invocamos cuantas veces sea necesario. Por ejemplo; si tenemos que desarrollar
un programa de álgebra, es muy probable que para distintos problemas necesi-
tamos calcular la norma de un vector, o el determinante de una matriz. En este
caso podemos hacer una función que será llamada cuando se la necesite.

1.1. Funciones
La estructura de una función es:

function nombre_funcion (param1:tipo1; ... ; paramN:tipoN): Tipo_retorno;


var
{ Bloque de declaración de variables locales a la función. }
{ El ámbito donde se pueden usar estas variables es solo }
{ entre el begin y end que marcan el comienzo y fin de la }
{ misma. }
begin
{ Bloque de código de la función. }
nombre_función := valorRetornoTipoRetorno;
{ Esta última lı́nea debe ejecutarse si o si antes de llegar }
{ al final de la función, no necesariamente justo antes. }
end;

Donde nombre_funcion será el nombre que usemos para indicarle a Pascal


que queremos que ejecute el código de la función. Tipo_retorno tiene que ser
un tipo de dato atómico (boolean, integer, real, char, etc), el cual tiene que ser
del mismo tipo que se le asigne a la función con valorRetornoTipoRetorno.
Y Bloque de código de la función son las sentencias para que la función
cumpla con el objetivo con el que fue creada.
La cantidad de parámetros (variables) pasados a la función van desde cero hasta
los permitidos por la capacidad de la memoria. Los tipos de estos parámetros,
a diferencia del Tipo_retorno, pueden ser de cualquier tipo, incluso tipos com-
puestos.

1.1.1. Pasaje de parámetros por valor y referencia


Existen dos formas de pasarle los parámetros a las funciones; por valor y por
referencia. Cuando pasamos un parámetro por valor el compilador crea una
copia exacta (para esto necesitamos más memoria) y se la pasa a la función; de

2
forma que si lo modificamos dentro de la función, esas diferencias no se verı́an
fuera de ella. Cuando pasamos un parámetro por referencia el compilador
crea una especie de apuntador a la variable original; por lo que cualquier modifi-
cación se reflejará en el bloque de código que lo invoco. Para pasar una variable
por referencia hay que anteponer la palabra reservada var a la variable; de la
forma: (var param:tipo-parametro);.
Las variables usadas en la sección bloque de código de la función pueden ser
las pasadas por parámetros y/o las variables locales a la función. En caso de
usar una variable declarada para el programa principal y que no sea pasada por
parámetros se dice que estas usando una variable global; cosa prohibida por la
cátedra.

1.1.2. Usos de las funciones


Para invocar la función es suficiente con poner el nombre de la función y
pasarle los parámetros necesarios; es importante recalcar que se deben pasar la
misma cantidad parámetros y del mismo tipo que fueron declarados cuando se
definió la función. No es necesario que tengan el mismo nombre.
Como las funciones retornan un valor, es posible (no obligatorio), asignarle ese
valor a una variable o usarlo en una expresión.

program ejemplo_funciones;
uses crt;

var { Variables globales a las que se puede acceder desde }


n, entero: integer; { cualquier parte del programa. }

function ejemplo (n : integer) : integer;


begin
...
end;

{ Programa Principal }
begin
...
{ Llamo a la función y guardo el resultado en la variable ’entero’. }
entero := ejemplo(n);

{ Llamo a la función sin guardar el resultado. }


ejemplo(n);

{ Llamo a la función pasandole el número 5. }


n := ejemplo (5);

{ Llamo a la función pasandole una variable con nombre }


{ distinto al puesto en la definición de la función. }
n := ejemplo(entero);

3
{ Uso la función en una expresión. }
if ( 7 = ejemplo() ) then ...

end.
Una posibilidad para asegurarse que no están usando accidentalmente una
variable global en el ámbito de una función (problema que puede generar noches
sin sueño por no encontrar un bug cerca de una entrega) es ubicar las variables
del programa principal después de las funciones, y justo antes del programa
principal.

program ejemplo_funciones;
uses crt;

function ejemplo (n : integer) : integer;


begin
...
end;

var { Variables globales a las que se puede acceder solo }


n, entero: integer; { en el Programa Principal. }

{ Programa Principal }
begin
...

entero := ejemplo(n);

...
end.

1.1.3. Parámetros actuales y parámetros formales


Los parámetros formales son aquellos que, como el nombre lo indica, le
dan forma a la función. Es decir, son aquellos que aparecen en su definción y se
usan para indicar la cantidad y tipos de parámetros que va a recibir la función.
Por ejemplo:

function nombre (param_formal1:tipo1; ... ; param_formalN:tipoN): tipo;


begin
...
end;

Los parámetros actuales son los que se están usando actualmente en la


ejecución del programa. Estos parámetros deben coincidir en su tipo, cantidad
y órden a los definidos en el encabezado de dicha función, ya que son los que
ocuparan el lugar reservado por los parámetros formales.

4
1.1.4. Ejemplo

program expo;
uses crt;
var
n: integer;
x: real;

{ Funcion que multiplica |n| veces a x, y si n era negativo,


lo invierte. Le paso por valor x y n; y retorna un real. }
function exponencial (x: real; n: integer):real;
var
i, modulo : integer;
resultado : real;
begin
resultado :=1;
{ Le calculo el modulo a n. }
modulo := abs(n);
{ Multiplico x las |n| veces. }
for i:=1 to (modulo) do
resultado := resultado∗x;

5
{ Si n era negativo, lo invierto. }
if (n<0) then resultado := 1 / resultado;
{ Retorno el resultado en exponencial. }
exponencial := resultado;
end;

begin
{ Limpio la pantalla. }
clrscr;
{ Imprimo mensajes y leo las variables. }
writeln(’Programa que calcula x a la n, con n entero’);
writeln;
write (’Ingrese x: ’);
readln (x);
write (’Ingrese n: ’);
readln (n);
{ Llamo a la funcion exponencial, pasandole como parametros
x y n, y guardando el valor de retorno en x. }
x := exponencial (x, n);
{ Imprimo el resultado y espero que ingresen una tecla. }
writeln (’El resultado es: ’,x :0:2);
readkey;
end.

1.1.5. Seguimiento de la función


Suponiendo que el usuario ingresa 3.5 para x y 2 para n:
Prog Ppal. Función exponencial Prog Ppal.

n x x n res i exponencial n x
2 3.5 3.5 2 61 61 2 6 3,5
6 3,5 2 12.15
12.25
12.5

1.2. Procedimientos
Si necesitamos una función que no retorne nada, usamos un procedimiento;
su estructura es:

procedure nombre_procedimiento (param1:tipo1; ... ; paramN:tipoN);


var
{ Bloque de declaración de variables locales al proc. }
begin
{ Sentencias del procedimiento. }
end;

6
1.3. Diferencias entre funciones y procedimientos
Hay que tener en cuenta que ahora que ya no tenemos la sentencia nombre_función := TipoRetorno;
y en el encabezado de la función cambiamos la palabra reservada function por
procedure, y eliminamos la parte ": Tipo_retorno". Otra diferencia con las
funciones es que ya no se puede usar en las expresiones.

1.4. Usos de los procedimientos


Para invocar el procedimiento solo tenemos que poner su nombre y pasarle
los parámetros necesarios. Como los procedimientos no retornan un valor, no
es posible usarlo en una expresión.

program ejemplo_proc;
uses crt;
var
cadena: string;

procedure ejemplo (linea : string);


begin
...
end;

{ Programa Principal }
begin
...
{ Llamo al procedimiento. }
ejemplo(cadena);
end.

2. Recursividad.
Se dice que algo es recursivo cuando en su definición se usa lo que estamos
tratando de definir. Por ejemplo, el factorial:


1 si n = 0
n! =
n ∗ (n − 1)! si n > 0
En este caso definimos el factorial de n, como n por el factorial de (n-1); si
n es distinto de 0; y 1 para n=0.

2.1. Estructura de una función o procedimiento recursivo


Una función recursiva siempre tiene dos partes fundamentales; la condición
de corte y la llamada recursiva. La llamada recursiva va a ser la que va a
invocar nuevamente a la función para que se siga ejecutando. En cambio, la

7
condición de corte, va a ser la encargada de frenar esa recursividad.
Generalmente, esa condición de corte está asociada a la estructura if, pero
puede darse el caso en que sea conveniente usar otra estructura selectiva; como
por ejemplo, un case. A su vez, cuando llega a esa condición de corte suele
inicializarse alguna variable, o preparar el ambiente para que se dé el retroceso,
pero tampoco es una regla que se debe cumplir siempre.

Hay que tener en cuenta que cuando se invoca a un procedimiento recursivo,


no solo se necesita espacio en memoria para él, sino también para todas sus lla-
madas recursivas. Por ejemplo, si queremos calcular recursivamente el factorial
de 1,000, vamos a necesitar 4 bytes para el valor de n. Pero como no sabemos
el valor del factorial de 999, vamos a llamar a la función para que nos calcule el
valor de ese factorial; y ası́ sucesivamente hasta que lleguemos a pedir el facto-
rial de 0. Por lo tanto, en algún momento, en memoria van a coexistir las 1,001
llamadas a la función factorial, y que, si ocupa 4 bytes cada variable entera,
estarı́amos ocupando 4,004 bytes. Es un número despreciable para los tamaños
de la memoria de ahora, pero tengamos en cuenta que eso fue porque comen-
zamos con un procedimiento que tan solo necesitaba 4 bytes para almacenar sus
variables. Como conlusión, hay que tener cuidado con la cantidad de memoria
que necesitemos en una fución recursiva ya puede crecer rápidamente.

2.2. Seguimiento de una función recursiva


Aprovechando que ya pusimos el código del factorial y que es una función
conocida por todos, calculemos (recursivamente) el factorial de 5.
Al ver el encabezado de la función nos damos cuenta que tenemos que pasarle un
parámetro n que será el número del cual queremos obtener el factorial, entonces
hacemos f actorial(5);

Pero ahora tenemos el problema que para calcular el factorial de 5, necesita-


mos el factorial de 4, entonces usamos la función factorial para saber ese valor:
f actorial(4);

8
Esperamos a que termine el f actorial(4); pero nos encontramos nuevamente con
que no sabemos cuanto vale el factorial de 3, entonces le pedimos a alguien que
si lo cepa que nos lo calcule: f actorial(3);

Creo que no hace falta que lo diga, pero por las dudas, ahora no sabemos cuanto
vale el factorial de 2, entonces esperemos y pidamosle a alguien que haga ese
trabajo por nosotros: f actorial(2);

Y si lo hicimos tantas veces, porque no una vez más; f actorial(1);

9
Pero seguimos sin saber cuanto es el factorial de 1, por lo que invocamos nue-
vamente a la función factorial, pero ahora le pasamos un 0: f actorial(0);

Y ahora si nos preguntaron algo que si sabemos!!, cúanto es el factorial de 0?.

La respuesta es 1, como lo dice la condición de corte; por lo que dejamos de


llamar recursivamente a la función factorial y le devolvemos el valor pedido a
quien nos lo pidió.
A partir de ahora vamos a ir volviendo nuestros pasos para atrás e ir terminando
cada una de las funciones que empezamos.

10
Con ese valor, ahora vamos a poder calcular el factorial de 1, que era 1 ∗
f actorial(0) = 1 ∗ 1 = 1

Recién ahora que tenemos cuanto vale el factorial de 1, se lo devolvemos a quien


nos lo habı́a pedido:

11
Con el valor del factorial de 1, podemos calcular el factorial de 2 y devolverselo
a quien nos lo habı́a pedido:

Con el valor del factorial de 2, podemos calcular el factorial de 3 y devolverselo


al factorial de 4 para que siga con su ejecución:

Ya casi terminamos, ahora con el valor del factorial de 3, calculamos el de 4


como: f actorial(4) = 4 ∗ f actorial(3) = 4 ∗ 6 = 24

12
Y finalmente llegamos a tener el factorial de 4, por lo que con esto solo nos
queda multiplicarlo por 5 para calcular el factorial que nos habı́an pedido en un
primer momento:

2.3. Tipos de Recursividad


En programación existen dos tipos de recursividad: directa o indirecta.

2.3.1. Recursividad Directa


La recursividad directa es cuando un procedimiento o una función se lla-
ma a si mismas; por ejemplo:

procedure ejemplo();
begin
Sentencias;
ejemplo();

13
más_sentencias;
end;

2.3.2. Recursividad Indirecta


La recursividad indirecta es cuando un procedimiento o función invoca
a otro procedimiento o función, y éste último vuelve a invocar al primero. Por
ejemplo:
procedure rec2(); forward;

procedure rec1();
begin
Sentencias;
rec2();
end;

procedure rec2();
begin
Sentencias;
rec1();
end;
Por la forma secuencial de compilar de Pascal es necesario declararle que más
adelante se definirá el procedimiento rec2 para que no marque un error en
tiempo de compilación. Por este motivo es que tuvimos que incluir la lı́nea:
procedure rec2(); forward;
antes del encabezado del procedimiento rec1().

2.4. Ejemplo

program ejemRec;
uses crt;

procedure recDirecta (entero : integer);


begin
if (entero = 1) then writeln (’El entero es: ’,entero)
else begin
recDirecta (entero −1);
writeln (’El entero es: ’,entero);
end;
end;

function factorial (n : integer): integer;


begin
if (n = 0) then factorial := 1
else factorial := n∗factorial (n−1);
end;

14
procedure B (entero : integer); forward;

procedure A (entero : integer);


begin
if (entero = 1) then writeln (’A’)
else B (entero−1);
end;

procedure B (entero : integer);


begin
if (entero = 1) then writeln (’B’)
else A (entero−1);
end;

var
n, fac : integer;
begin
clrscr;
writeln (’Ejemplo de funciones recursivas.’);
writeln;
n := 5;
fac := factorial(5);
writeln (’El factorial de ’,n,’ es ’, fac,’.’);
writeln;
n := 10;
writeln (’Entramos a la funcion recDirecta con n = ’, n,’.’);
recDirecta(n);
writeln;
n := 4;
write(’Llamado a la funcion A (’,n,’): ’);
A(n);
write(’Llamado a la funcion B (’,n,’): ’);
B(n);
n := 5;
write(’Llamado a la funcion A (’,n,’): ’);
A(n);
write(’Llamado a la funcion B (’,n,’): ’);
B(n);
readkey;
end.

15
Índice
1. Introducción a procedimientos y funciones. 2
1.1. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1. Pasaje de parámetros por valor y referencia . . . . . . . . 2
1.1.2. Usos de las funciones . . . . . . . . . . . . . . . . . . . . . 3
1.1.3. Parámetros actuales y parámetros formales . . . . . . . . 4
1.1.4. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.5. Seguimiento de la función . . . . . . . . . . . . . . . . . . 6
1.2. Procedimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3. Diferencias entre funciones y procedimientos . . . . . . . . . . . . 7
1.4. Usos de los procedimientos . . . . . . . . . . . . . . . . . . . . . 7

2. Recursividad. 7
2.1. Estructura de una función o procedimiento recursivo . . . . . . . 7
2.2. Seguimiento de una función recursiva . . . . . . . . . . . . . . . . 8
2.3. Tipos de Recursividad . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.1. Recursividad Directa . . . . . . . . . . . . . . . . . . . . . 13
2.3.2. Recursividad Indirecta . . . . . . . . . . . . . . . . . . . . 14
2.4. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

16

También podría gustarte