Procedimientos, Funciones y Recursividad Gonzalo Soriano gsorianob@
[email protected] 9 de junio de 2009
”Divide y vencer´ as”
1.
Intr Introdu oducc cci´ i´ on a procedimientos y funciones. on
Un procedimiento o funci´on on es un bloque de c´odigo odigo que no se encuentra en el bloque blo que o programa principal; p ero puede ser invocado por p or ´este este cuantas veces quiera el programador. Cuando tenemos que resolver un gran programa, muchas veces es muy ´util util dividirlo en varios peque˜nos nos problemas; de esta forma resolvemos m´as as problemas, pero m´as as sencillos. Tambi´en en es muy util u ´ til usar procedimientos y funciones cuando una tarea la tenemos 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 ´algebra, algebra, es muy probable que para distintos problemas necesitamos calcular la norma de un vector, o el determinante de una matriz. En este caso podemos hacer una funci´on on que ser´a llamada cuando se la necesite.
1.1. 1.1.
Funcio uncione ness
La estructura de una funci´on on es: function function nombre_fu nombre_funcion ncion (param1:ti (param1:tipo1; po1; ... ; paramN:tip paramN:tipoN): oN): Tipo_reto Tipo_retorno; rno; var { Bloque Bloque de declar declaraci aci´ ´ on on de variab variables les locales locales a la funci´ funci´ on. o n. } { El ´ ambi a mbito to dond donde e se pueden pueden usar usar esta estas s variab variable les s es solo } { entr entre e el begi begin n y end end que que marc marcan an el comie comienz nzo o y fin fin de la } { mi misma. } begin { Bloq Bloque ue de c´ odigo odigo de la funci´ funci´ on. o n. } nombre_funci´ on on := valorReto valorRetornoTi rnoTipoRet poRetorno; orno; { Esta ´ ultima ultima l´ ınea ı nea debe debe ejec ejecut utar arse se si o si antes antes de llegar llegar } { al final de la funci´ on, o n, no nece necesa sari riam amen ente te just justo o ante antes. s. } end;
Donde nombre_funcion ser´a el nombre que usemos para indicarle a Pascal que queremos que ejecute el c´odigo odigo de la funci´on. on. Tipo_retorno tiene que ser un tipo de dato at´omico omico (boolean, integer, real, char, etc), el cual tiene que ser del mismo tipo que se le asigne a la funci´on on con valorRetornoTipoRetorno. Y Bloq on Bloque ue de c´ odigo odigo de la funci´ funci´ on on son las sentencias para que la funci´on cumpla con el objetivo con el que fue creada. La cantidad de par´ametros ametros (variables) pasados a la funci´on on van desde cero hasta los permitidos por la capacidad de la memoria. Los tipos de estos par´ametros, ametros, a diferencia del Tipo_retorno, pueden ser de cualquier tipo, incluso tipos compuestos. Cuando en la funci´on on hacemos: nombre_funci´ on on := valorReto valorRetornoTi rnoTipoRet poRetorno; orno; le estamos asignando un valor a esa funci´on on para que devuelva, pero hay que tener en cuenta que una vez que le asignamos ese valor no se le puede cambiar; por lo menos por esa llamada.
2
1.1. 1.1.1. 1.
Pasaje Pasaje de par´ par´ ametros por valor y referencia ametros
Existen dos formas de pasarle los par´ametros ametros a las funciones; por valor y por referencia. Cuando pasamos un par´ametro amet ro por po r valor el compilador crea una copia exacta (para esto necesitamos m´as as memoria) y se la pasa a la funci´on; de forma que si lo modificamos dentro de la funci´on, on, esas diferencias diferen cias no se s e ver´ıan ıan fuera de ella. Cuando pasamos un par´ ametro ametro por referencia refere ncia el compilador crea una especie de apuntador a la variable original; por lo que cualquier modificaci´ on on se reflejar´a en el bloque de c´odigo odigo 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); param:tipo-parametro);. Las variables usadas en la secci´on on bloque de c´odigo odigo de la funci´on on pueden ser las pasadas por par´ametros ametros y/o las variables locales a la funci´on. on. En caso de usar una variable declarada para el programa principal y que no sea pasada por par´ ametros se dice que estas usando una variable global; cosa prohibida por la ametros c´atedra. atedra.
1.1. 1.1.2. 2.
Usos Usos de las las func funcio ione ness
Para invocar la funci´on on es suficiente con poner el nombre de la funci´on on y pasarle los par´ametros ametros necesarios; es importante recalcar que se deben pasar la misma cantidad par´ametros ametros y del mismo tipo que fueron declarados cuando se defini´ o la funci´on. on. 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´on. program ejemplo_funciones; ejemplo_funciones; uses crt; var { Var Variab iables les glo glob bale ales a las las que que se pue pued de acc accede eder des desde } n, entero: integer; { cualquier parte del programa. } functi function on ejempl ejemplo o (n : intege integer) r) : intege integer; r; begin ... end; { Progra Programa ma Princi Principal pal } begin ... { Llamo a la funci´on on y guardo guardo el result resultado ado en la variab variable le ’enter ’entero’. o’. } entero entero := ejemplo(n) ejemplo(n); ; { Llamo a la funci´on on sin guardar guardar el result resultado ado. . } ejemplo(n); { Llamo a la funci´on on pas´ pas´ andole andole el n´ umer u mero o 5. } n := ejem ejempl plo o (5); (5);
3
{ Llamo a la funci´on on pas´ pas´ ando a ndole le una varia variabl ble e con con nomb nombre re { distin distinto to al puesto puesto en la defini definici´ ci´ on o n de la funci´ funci´ on. n := ejemplo(e ejemplo(enter ntero); o);
} }
{ Uso Uso la funci´ funci´ on on en una expresi´ expresi´ on. o n. } if ( 7 = ejem ejempl plo( o() ) ) then then ... end.
Una posibilidad para asegurarse que no est´an an usando accidentalmente una variable global en el ´ambito ambito de una funci´on on (problema que puede generar noches sin sue˜ no no por no encontrar un bug cerca de una entrega) es ubicar las variables del programa programa principal principal despu´ despu´es es de las funciones, funciones, y justo antes antes del programa programa principal. program ejemplo_funciones; ejemplo_funciones; uses crt; functi function on ejempl ejemplo o (n : intege integer) r) : intege integer; r; begin ... end; var { Var Varia iab bles les glo glob bale ales a las las que que se se pue pued de acc acced ede er sol solo o } n, ente entero ro: : int integ eger er; ; { en en el el Pro Progr gram ama a Pri Princ ncip ipal al. . } { Progra Programa ma Princi Principal pal } begin ... entero entero := ejemplo(n) ejemplo(n); ; ... end. 1.1. 1.1.3. 3.
Par´ ar´ ametros ametros actuales actual es y par´ p ar´ ametros ametros formales formale s
Los par´ametros amet ros formale for maless son aquellos que, como el nombre lo indica, le dan forma a la funci´on. on. Es decir, son aquellos que aparecen en su definici´on on y se usan para indicar la cantidad y tipos de par´ametros ametros que va a recibir la funci´on. on. Por ejemplo: function function nombre nombre (param_for (param_formal1: mal1:tipo1 tipo1; ; ... ; param_for param_formalN: malN:tipoN tipoN): ): tipo; begin ... end;
Los par´ametros an an usando actualmente en la amet ros actual act uales es son los que se est´ ejecuci´ on del programa. Estos par´ametros on ametros deben coincidir en su tipo, cantidad 4
y orden a los definidos en el encabezado de dicha funci´on, on, ya que son los que reservado ocuparan el lugar por los par´ametros ametros formales.
1.1. 1.1.4. 4.
Ejemp Ejemplo lo
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); 5
{ Multiplico x las |n | veces. } for i:=1 to (modulo) do resultado := resultado ∗x; { 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. 1.1.5.
Seguim Seguimien iento to de de la funci´ funci´ on on
Suponiendo que el usuario ingresa 3.5 para x y 2 para n: Prog Ppal. n 2
x 3.5
Funci´on exponencial x 3.5
n 2
res 1 3,5 12.25
i 1 2
exponencial
Prog Ppal. n 2
x 3,5 12.15
12.5
1.2. 1.2.
Proce Procedi dimi mien ento toss
Si necesitamos una funci´on on que no retorne nada, usamos un procedimiento; su estructura es: procedure procedure nombre_pro nombre_procedi cedimient miento o (param1:t (param1:tipo1; ipo1; ... ; paramN:ti paramN:tipoN); poN); var { Bloque Bloque de declar declaraci aci´ ´ on on de variab variables les locales locales al proc. proc. } begin { Sentencia Sentencias s del procedimi procedimiento. ento. }
6
end;
1.3. 1.3.
Diferen Diferencias cias entre entre funci funcione oness y procedimien procedimientos tos
Hay que tener en cuenta que ahora que ya no tenemos la sentencia nombre_funci´ on on := TipoRetor TipoRetorno; no; y en el encabezado de la funci´on on cambiamos la palabra reservada function por procedure, y eliminamos la parte ": Tipo_reto Tipo_retorno" rno". Otra diferencia con las funciones es que ya no se puede usar en las expresiones.
1.4. 1.4.
Usoss de los Uso los proc proced edim imien iento toss
Para invocar el procedimiento solo tenemos que poner su nombre y pasarle los par´ametros ametros necesarios. Como los procedimientos no retornan un valor, no on. es posible usarlo en una expresi´on. program ejemplo_proc; uses crt; var cadena: cadena: string; string;
procedure procedure ejemplo (linea : string); string); begin ... end; { Progra Programa ma Princi Principal pal } begin ... { Llamo Llamo al proced procedimi imient ento. o. } ejemplo(cadena); end.
2.
Recu Recurs rsiv ivid idad ad..
Se dice que algo es recursivo cuando en su definici´on se usa lo que estamos tratando de definir. Por ejemplo, el factorial:
n! =
1
si n = 0 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.
7
2.1. 2.1.
Estru Estruct ctur ura a de una una func funci´ i´ on o procedimiento recursivo on
Una funci´on on recursiva siempre tiene dos partes fundamentales; la condici´on de corte y la llamada recursiva. La llamada recursiva recursiva va a ser la que va a invocar nuevamente a la funci´on on para que se siga ejecutando. En cambio, la condici´ on on de corte, va a ser la encargada de frenar esa recursividad. Generalmente, esa condici´on on de corte est´a 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´on de corte suele inicializarse alguna variable, variable, o preparar el ambiente para que se d´e 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 ´el, el, sino tambi´ t ambi´ en en para todas t odas sus llamadas 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´on para que nos calcule el valor de ese factorial; y as´ as´ı sucesivamente hasta que lleguemos a pedir p edir el factorial de 0. Por lo tanto, en alg´un un momento, en memoria van a coexistir las 1,001 llamadas a la funci´on on factorial, y que, si ocupa 4 bytes cada variable entera, estar´ est ar´ıamos ıam os ocupa o cupando ndo 4,004 bytes. Es un n´umero umero despreciable para los tama˜nos nos de la memoria de ahora, pero tengamos en cuenta que eso fue porque comenzamos con un procedimiento que tan solo necesitaba 4 bytes para almacenar sus variables. Como conclusi´on, on, hay que tener cuidado con la cantidad de memoria que necesitemos en una funci´on on recursiva ya que puede crecer r´apidamente. apidamente.
2.2. 2.2.
Segui Seguimie mien nto de de una una func funci´ i´ on on recursiva
Aprovechando que ya pusimos el c´odigo odigo del factorial y que es una funci´on on conocida conocida por todos, calculemos calculemos (recursiv (recursivamen amente) te) el factorial factorial de 5. Al ver el encabezado de la funci´on on nos damos cuenta que tenemos que pasarle un par´ ametro ametro n que ser´a el n´ umero del cual queremos obtener el factorial, entonces umero hacemos factorial (5);
8
Pero ahora tenemos el problema que para calcular el factorial de 5, necesitamos el factorial de 4, entonces usamos la funci´on factorial para saber ese valor: factorial (4);
Esperamos a que termine el factorial (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: factorial (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: factorial (2);
Y si lo hicimos tantas veces, porque no una vez m´as; as; factorial (1);
9
Pero seguimos sin saber cuanto es el factorial de 1, por lo que invocamos nuevamente a la funci´on on factorial, pero ahora le pasamos un 0: factorial (0);
Y ahora si nos preguntaron algo que si sabemos!!, cu´anto anto es el factorial de 0?.
10
La respuesta es 1, como lo dice la condici´on on de corte; por lo que dejamos de llamar recursivamente a la funci´on on factorial y le devolvemos el valor pedido a quien nos lo pidi´o. o. A partir de ahora vamos a ir volviendo nuestros pasos para atr´as e ir terminando cada una de las funciones que empezamos.
Con Con ese ese valor alor,, ahor ahoraa vam amos os a poder poder calc calcul ular ar el fact factor oria iall de 1, que que era era 1 ∗ factorial (0) = 1 ∗ 1 = 1
11
Reci´ en en ahora que tenemos cuanto vale el factorial de 1, se lo devolvemos a quien nos lo hab´ hab´ıa pedido: pedid o:
Con el valor del factorial de 1, podemos calcular el factorial de 2 y devolv´ erselo erselo a quien q uien nos lo hab´ hab´ıa pedido: pedi do:
12
Con el valor del factorial de 2, podemos calcular el factorial de 3 y devolv´ erselo erselo al factorial de 4 para que siga con su ejecuci´on: on:
Ya casi terminamos, ahora con el valor del factorial de 3, calculamos el de 4 como: factorial (4) = 4 ∗ factorial (3) = 4 ∗ 6 = 24
13
Y finalmente llegamos a tener el factorial de 4, por lo que con esto solo nos queda multiplicarlo por p or 5 para calcular el factorial que nos hab´ıan ıan pedido en un primer momento:
2.3. 2.3.
Tipos Tipos de Recur Recursiv sivid idad ad
En programaci´on on existen dos tipos de recursividad: directa o indirecta.
2.3.1. 2.3.1.
Recurs Recursivi ividad dad Direct Directa a
La recursividad directa es cuando un procedimiento o una funci´on on se llama a si mismas; por ejemplo: procedure ejemplo(); begin Sentencias; ejemplo();
14
m´ as_sentencias; as_sentencias; end; 2.3.2. 2.3.2.
Recurs Recursivi ividad dad Indire Indirecta cta
La recursividad indirecta es cuando un procedimiento o funci´on on invoca a otro procedimiento o funci´on, on, y ´este este ultimo u ´ ltimo vuelve a invocar al primero. Por ejemplo: procedure procedure rec2(); rec2(); forward; 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´as adelante se definir´a el procedimien procedimiento to rec2 para que no marque un error en tiempo de compilaci´ compilaci´ on. on. Por este motivo es que tuvimos que incluir la l´ınea: procedure procedure rec2(); rec2(); forward; forward;
antes del encabezado del procedimiento rec1().
2.4. 2.4.
Ejem Ejempl plo o
program ejemRec;
uses crt; procedure recDirecta (entero : integer ); begin if (entero = 1) then writeln 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;
15
procedure B (entero : integer); forward; procedure A (entero : integer); begin if (entero = 1) then writeln writeln (’A’) else B (entero−1); end; procedure B (entero : integer); begin if (entero = 1) then writeln 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.
16
´ Indice 1. Introducci´ Introducci´ on a procedimientos y funciones.
1.1. Funciones . . . . . . . . . . . . . . . . . . . . . . . 1.1.1. 1.1 .1. Pasaje Pasaje de de par´ par´ a metro amet ross por por valor lor y refer eferen enccia 1.1.2. Usos de las funciones . . . . . . . . . . . . . 1.1.3. 1.1 .3. Par´ Par´ ametros actuales y par´ametros formales ametros 1.1.4. Ejemplo . . . . . . . . . . . . . . . . . . . . 1.1.5. 1.1.5. Seguimient Seguimiento o de la funci´ funci´ on . . . . . . . . . . 1.2. Procedimientos . . . . . . . . . . . . . . . . . . . . 1.3. 1.3. Dife Difere renc ncia iass en entre tre func funcio ione ness y proc proced edim imie ien ntos tos . . . . 1.4. Usos de los proce ocedimientos . . . . . . . . . . . . .
2
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
2.1. Estructura Estructura de una funci´ funci´on o n o proc proced edim imie ien nto recu recurs rsiv ivoo 2.2. Seguimien Seguimiento to de una una funci´ funci´ on recursiva . . . . . . . . . 2.3. Tipos pos de Recursividad . . . . . . . . . . . . . . . . . 2.3.1. Recursividad Directa . . . . . . . . . . . . . . 2.3.2. Recursividad Indirecta . . . . . . . . . . . . . 2.4. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
2. Recursividad.
2 2 3 4 5 6 6 7 7 7
17
7 8 13 13 14 14