Está en la página 1de 12

Universidad de Santiago de Chile Facultad de Ingeniera Departamento de Ingeniera Informtica

Autor: Csar Silva

cesar.silva@usach.cl

1.

Resumen

En el presente documento se abordan las temticas de funciones y recursin, asumiendo que se recuerdan los conceptos de variables y todo lo que es anterior a este resumen. Arreglos y matrices se abordarn ms adelnte.

2.

Funciones

Una funcin es una seccin de cdigo, o un conjunto de lneas de cdigos agrupados bajo un mismo nombre, que tiene un fin determinado y que puede repetirse cuantas veces se requiera.

Grficamente, podemos ver una funcin como:

Parmetros

Funcin

Retorno

Figura 2.1: Funcin como caja negra.

Para definir una funcin, debe realizarse entre la declaracin de la clase, y la declaracin del main. Es decir:

import java.util.Scanner;

public class NombreClase{ 2 /************************** Aca declaran las funciones! 3 ***************************/ public static void main(String[] args){
4

} } Figura 2.2: Donde declarar una funcin.

En la figura 2.2, se observan diferentes partes en la declaracin: 1. Seccin de importaciones, es decir, cuando requerimos utilizar alguna funcionalidad, como Scanner, que no est nativa en java. 2. Declaracin de la clase, que es donde comienza el archivo donde est nuestro cdigo. El NombreClase debe ser igual al nombre del archivo java, esto es: NombreClase.java 3. Ac es donde se declaran las funciones! Se ver ms adelante el formato de esto, pero es importante destacar que pueden declararse tantas funciones como se requiera, siempre y cuando se respete el formato de la misma. 4. Ac se escribe el programa en s, como siempre se ha hecho.

La declaracin de la funcin tiene la siguiente estructura:

static TipoRetorno NombreFuncin( Parmetros ){ CdigoFuncin }

Figura 2.3: Declarando una funcin. TipoRetorno: corresponde a la definicin del retorno de la funcin. Esto corresponde a lo que entrega la misma, es decir, el resultado que se espera. Algunos retornos posibles son: int, booelan, String, double, char, y otros tipos, y uno especial llamado void que indica que la funcin no retornar ningn valor. NombreFuncin : es el nombre para la funcin. Puede ser cualquiera, de preferencia representativo de lo que realiza la misma. Parmetros: son las entradas a la funcin, los valores con los cuales se trabajar. Pueden ser 0, 1, 2 o ms. Deben declararse como o ( tipo nombreParmetro1, tipo nombreParmetro2 . ) No es necesario que los tipos sean iguales entre s, puede tenerse diferentes parmetros con diferentes tipos, y tampoco es estrictamente necesario que coincidan con el tipo de retorno. Todos pueden ser diferentes entre s, ya que lo que se declara en esta parte es ms que nada para recordar que significa cada variable. CdigoFuncin: Aqu se coloca el cdigo correspondiente a la funcin. Es necesario destacar que si el TipoRetorno es cualquiera distinto a void debe existir en todos los caminos posibles un retorno. Veamos un ejemplo: static int Ejemplo(int n){ if(n < 0){ return 0; } else{ return n; } } Figura 2.4: Ejemplo de retornos. Si observamos la funcin Ejemplo, sabemos que retorna un entero, por tanto, debemos colocar un return. Sin embargo, si lo colocamos solo en el if, ocurre que cuando entremos al else no tendramos retorno, por tanto, debemos colocar return en todos los camino posibles. 3

Para utilizar una funcin, por ejemplo, llamarla desde el main, debe realizarse de la siguiente manera:

public class NombreClase{ static int FuncionSuma(int sumando1, int sumando2){ return sumando1 + sumando2; } public static void main(String[] args){ int a = 10; int b = 20; int resultado = FuncionSuma( a , b ); System.out.println("El resultado es: "+resultado); } } Figura 2.5: Llamando una funcin. En la figura 2.4 se observa la definicin de una funcin, llamada FuncionSuma, la que recibe dos parmetros: sumando1 y sumando2. Adems, retorna un elemento del tipo int.

El llamado es realizado en la funcin main en su tercera lnea, con dos parmetros definidos en la misma. La ejecucin de una funcin puede verse como sigue: main func

return

Figura 2.6: Diagrama ilustrativo de uso de funciones.

En la figura 2.4 se muestra de forma grfica que, al momento en que se llama a una funcin cualquiera sea esta, se ejecutar hasta el fin de la misma, es decir, hasta alcanzar un retorno.

Una funcin puede estar compuesta por cualquier elemento en su cuerpo, tal cual se puede programar en el main. Inclusive puede hacer llamados a funciones. En la figura 2.5 se muestra un ejemplo de esto, qu resultado se obtendra de dicha ejecucin?

public class NombreClase{ static int FuncionSuma(int sumando1, int sumando2){ return sumando1 + sumando2; } static int FuncionMultiplicacion (int coeficiente1, int coeficiente2){ int acumulador = 0; while( coeficiente2 > 0 ){ acumulador = FuncionSuma( coeficiente1, acumulador ); coeficiente2 = coeficiente2 - 1; } return acumulador; } public static void main(String[] args){ int a = 10; int b = 5; int resultado = FuncionMultiplicacion ( a , b ); System.out.println("El resultado es: "+resultado); } }

Figura 2.7: Usando funciones en funciones.

Para identificar que est realizando este cdigo, es conveniente identificar sus partes. Primero, vemos que entre la declaracin de la clase y la funcin main existen dos funciones: FuncionSuma y FuncionMultiplicacion. Segundo, vemos que FuncionSuma recibe dos parmetros enteros, sumando1 y sumando2 y retorna un valor entero, correspondiente a la suma de estos valores. Tercero, la FuncionMultiplicacion recibe dos parmetros enteros, coeficiente1 y coeficiente2 y retorna un valor entero. o Si observamos con ms detalle el cdigo de esta funcin, vemos que posee una variable entera acumulador, y luego comienza un ciclo while. 5

o Este ciclo est limitado por uno de los parmetros de entrada, coeficiente2, y se realizar mientras el valor de esta variable sea mayor a 0. o Dentro del ciclo, la variable acumulador recibe el resultado de aplicar la FuncionSuma, con dos parmetros: coeficiente1 y acumulador. Como acumulador inicia con un valor 0, la primera suma que se realice ser coeficient e1 + ( 0 ), donde ( 0 ) corresponde al valor que tiene la variable acumulador, y luego, las sucesivas sern: coeficiente1 + ( coeficiente1 ) coeficiente1 + ( coeficiente1 + coeficiente1 ) coeficiente1 + ( coeficiente1 + coeficiente1 + + coeficiente1 ) o Esto se repetir coeficiente2 -veces. Una vez que coeficiente2 llega a 0, se acaba el ciclo y se retorna el valor acumulado hasta ese momento. Cuarto, en la funcin main se realiza el llamado a la FuncionMultiplicacion con dos valores predeterminados: 10 y 5. Entonces, cul es el resultado?

Cabe destacar que este ejemplo no funciona para todos los casos. Qu sucede si coeficiente2 es un valor menor a 0, por ejemplo, -1? Es importante reconocer todos los casos posibles, y cules de estos se deben abarcar para completar satisfactoriamente el desarrollo de un problema.

3.

Recursin

Si entendemos el concepto de funcin, la recursin se torna sencilla. Antes de definir como realizarla, hay que tener claros dos conceptos: Iteracin: corresponde al uso de ciclos para resolver un problema. Estrictamente la iteracin es la ejecucin de un conjunto de lneas de forma repetida, es decir, una o ms veces. En la figura 3.1 vemos un ejemplo de iteracin. Se aprecia en ella que el clculo se realiza dentro de un ciclo for.

static int FactorialIterativo(int numero){ int resultado = 1; if(numero < 0){ System.out.println("No existe el factorial de un negativo"); return 0; } else if(numero == 0){ return resultado; } else{ for(int i=1; i<=numero; i++){ resultado = resultado * i; } return resultado; } } Figura 3.1: Funcin factorial en su versin iterativa. Recursin: corresponde a la utilizacin de la misma funcin para la resolucin de un problema. Esto quiere decir que, la idea de ciclo no est presente, y esto se da mediante el llamado a la misma funcin pero con algn valor modificado. Veamos un ejemplo de esto. static int FactorialRecursivo(int numero){ if(numero==0){ return 1; } return numero * FactorialRecursivo(numero - 1); } Figura 3.2: Funcin factorial en su versin recursiva.

Analicemos esto ltimo. Existen dos componentes vitales para su realizacin. El primero, tiene que ver con la condicin de trmino, es decir, aquel momento en que la funcin llega a su menor expresin, su lmite inferior, un valor numrico que es el menor posible, en este caso, es el factorial de 0 con resultado 1. El otro componente, es el llamado a la misma funcin, el cual debe incluir la disminucin de al menos UNO de los parmetros.

static int FactorialRecursivo(int numero){ if(numero==0){ Condicin de trmino return 1; } return numero * FactorialRecursivo(numero - 1); }
Llamado a la misma funcin con el parmetro modificado numero-1

Figura 3.3: Identificacin de las partes principales en recursin.

Sobre la condicin de trmino es importante recalcar que esta no necesariamente es 1, pueden ser ms dependiendo del problema. Tambin es necesario comprender que no siempre el retorno de la condicin de trmino es un nico nmero, puede ser una resta, o multiplicacin, pero necesariamente deber ser un valor numrico.

La recursin es, en teora, ms cercana al lenguaje matemtico que conocemos. Esto la hace ms sencilla a la hora de definir problemticas matemticas que, para la iteracin, sera ms complicado por el nivel de abstraccin que supone. Veremos a continuacin algunos ejemplos (extrados de las presentaciones de laboratorio).

3.1

Fibonacci

La serie de Fibonacci se construye como la suma de los dos anteriores, es decir, si comenzamos con dos valores iniciales, 0 y 1, obtenemos: 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21, Si ordenamos esto, con n como la posicin en la serie de Fibonacci y F(n) como el valor de dicha posicin, obtenemos lo siguiente:

n F(n)

0 0

1 1

2 1

3 2

4 3

5 5

6 8

7 13

N (N-2) + (N-1)

Figura 3.4: Serie de Fibonacci.

Lo que es equivalente a tener: Fibonacci( n ) = Fibonacci( n 2 ) + Fibonacci( n 1) [1]

3.1.1 Fibonacci Recursivo

Para generar la recursin, debemos identificar las dos partes vitales: condicin de trmino y llamado a la misma funcin. Analicemos lo primero.

Qu condicin o condiciones de trmino requerimos para este problema? Si vemos la figura 3.4, Fibonacci se construye como una serie de sumas de los dos nmeros anteriores, por tanto, para iniciar nuestra serie requerimos obligatoriamente de dos valores previos: Fibonacci(0) y Fibonacci(1). No existen nmero previos a 0, por tanto esa posicin no se puede calcular, y como solo disponemos de Fibonacci(0), no se puede calcular Fibonacci(1), de esto se desprende que, como condiciones posibles de trminos sern: cuando n = 0 y cuando n = 1. Cul debemos usar?

Podramos pensar en utilizar la condicin ms pequea en busca de abarcar la mayor cantidad de casos posibles, por tanto, diramos if( n = 0 ) return 0;. Sin embargo, esto nos plantea una pregunta: Si calculamos como en [1], si usamos n = 1, entonces deberamos calcular: Fibonacci( 1 ) = Fibonacci( -1 ) + Fibonacci( 0 ), es esto posible?

Pero, deberamos estar bien si es que no calculamos el Fibonacci( 1 ), no es as?. NO! Lamentablemente, la serie de Fibonacci est compuesta de nmeros anteriores, por tanto: Fibonacci( 3 ) = Fibonacci( 1 ) + Fibonacci( 2 )

Entonces, para evitar este problema, debemos colocar ms de una condicin de trmino, ya que por definicin, la mnima suma que podemos lograr es la de Fibonacci( 2 ). De esto se desprende que nuestras condiciones de trmino sern para n = 0 Y para n = 1.

Veamos ahora el llamado a la misma funcin. La definicin de la serie de Fibonacci se ve en [1], por tanto parece trivial explicar este punto.

De lo anterior, podemos crear la siguiente funcin:

static int FibonacciRecursivo(int n){ if(n == 0){ return 0; } if(n == 1){ return 1; } return FibonacciRecursivo(n-2) + FibonacciRecursivo(n-1); }

Figura 3.5: Fibonacci Recursivo.

10

3.1.2 Fibonacci Iterativo

Ahora veamos el caso iterativo. Sabemos que se compone con la suma de los dos anteriores, y tenemos dos valores bien definidos, para 0 y 1. Sabemos tambin que el resultado final ser la acumulacin de sumas sucesivas, y que estas se realizaran (n-1) veces, veamos el siguiente cdigo:

static int FibonacciIterativo(int n){ int nMenos2 = 0; int nMenos1 = 1; int acumulado = 0; for(int i = 1; i < n; i++){ acumulado = nMenos2 + nMenos1; nMenos2 = nMenos1; nMenos1 = acumulado; } return acumulado; } Figura 3.6: Fibonacci Iterativo.

Lo importante y complejo de ver, es la necesidad de actualizar los valores en cada iteracin. Esto se hace en nMenos2 = nMenos1 y nMenos1 = acumulado. El primero, actualiza el valor de (n 2) con el valor de (n 1), y el valor de (n 1) con el valor de (n), ya que en la siguiente iteracin, estos valores corresponden a los dos trminos anteriores.

11

3.2.1 Funcin de Ackermann

Veamos un ejemplo donde la solucin recursiva es ms sencilla que la iterativa. Se nos dice que la funcin de Ackermann se contruye como sigue:

Figura 3.7: Funcin de Ackermann. Si transcribimos lo anterior a cdigo, sin analizar mucho, obtenemos lo que se observa en la figura 3.8.

static int Ackermann(int m, int n){ if(m == 0){ return n + 1; } else if((m > 0) && (n = 0)){ return Ackerman(m - 1, 1); } else if((m > 0) && (n > 0)){ return Ackermann(m - 1, Ackermann(m, n - 1)); } else{ return -1; } } Figura 3.7: Codificacin recursiva de la funcin de Ackermann.

La nica diferencia es que se agrega un else{ return -1; } ya que es necesario para tener retornos en todos los casos posibles.

Ahora, puede hacerlo iterativo?Ser tan directo el traspaso a cdigo?Realmente se puede hacer iterativo?

12

También podría gustarte