"Hola, Amigo. Hoy Bilaabo te hablará sobre la recursividad".

Recursividad - 1

Como sabes, en Java algunos métodos llaman a otros métodos. Además, cuando se llama a un método, se le pasan argumentos específicos, pero las variables locales del método toman ciertos valores mientras se ejecuta.

"UH Huh."

"Y como sabes, las variables internas de diferentes métodos son independientes entre sí".

"UH Huh."

"Así que imagina la situación en la que un método se llama a sí mismo. Esto se llama recursividad. Por ejemplo:"

Ejemplo
public static void main(String[] args)
{
 countDown(10);
}

public static void countDown(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown(x - 1);
 }
}
Salida de pantalla:
10
9
8
7
6
5
4
3
2
1
Boom!

"Puedo ver que el método se llama a sí mismo en el código, pero honestamente no entiendo qué está pasando".

"Bueno, casi lo mismo que sucede cuando se llama a un método diferente".

"No, estoy preguntando qué sucede con las variables? ¿Con sus valores? ¿Y cómo salimos del método? ¿O salimos de todo de una vez?"

"Dios mío. Todo es mucho más simple. Imagina que el método que se llama a sí mismo se ha multiplicado muchas veces. Entonces tendríamos la situación análoga:"

Llamada de método recursivo lo que realmente sucede
public static void main(String[] args)
{
 countDown(10);
}

public static void countDown(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown(x - 1);
 }
}
public static void main(String[] args)
{
 countDown1(10);
}

public static void countDown1(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown2(x - 1);
 }
}
public static void countDown2(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown3(x - 1);
 }
}
public static void countDown3(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown4(x - 1);
 }
}

public static void countDown4(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown5(x - 1);
 }
}
Salida de pantalla: Salida de pantalla:
3
2
1
Boom!
3
2
1
Boom!

"En otras palabras, cada vez que se llama a un método (incluso por sí mismo), se crean nuevas variables que almacenan los datos de este método. No hay variables compartidas".

"Con cada llamada, se crea en la memoria otra copia de los argumentos del método, con nuevos valores. Cuando volvemos al método anterior, sus variables se usan allí. En otras palabras, durante la recursividad en realidad llamamos a otro método, pero con el mismo código que el nuestro! "

"Ya veo. ¿Y cómo funciona salir de esos métodos? ¿Tal vez un ejemplo?"

"OK. Un ejemplo vale más que mil palabras. "Aquí está tu ejemplo:"

Llamada de método recursivo Llamada de método recursivo
public static void main(String[] args)
{
 print(3);
}

public static void print(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print(x - 1);
  System.out.println(x);
 }
}
public static void main(String[] args)
{
 print1(3);
}

public static void print1(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print2(x - 1);
  System.out.println(x);
 }
}

public static void print2(int x)
{
 if (x <= 0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print3(x - 1);
  System.out.println(x);
 }
}
Salida de pantalla: Salida de pantalla:
3
2
1
Boom!
1
2
3
3
2
1
Boom!
1
2
3

"Está bien. Creo que entiendo. ¿Por qué necesitamos la recursividad?"

"Hay muchísimas tareas que se pueden dividir en subtareas separadas que son idénticas a la tarea original. Por ejemplo, debe recorrer todos los elementos de un árbol XML. Cada elemento puede tener varios elementos secundarios, y estos tienen su propios elementos secundarios".

"O necesita mostrar la lista de archivos que están en un directorio y todos sus subdirectorios. Así que escribe un método que muestra los archivos del directorio actual. Luego, para obtener los archivos de todos los subdirectorios, llama a su método usando un argumento diferente: un subdirectorio".

"Por ejemplo:"

Mostrar todos los archivos en un directorio y sus subdirectorios
public static void main(String[] args)
{
 printAllFiles(new File("c:/windows/"));
}

public static void printAllFiles(File dir)
{
 for (File file : dir.listFiles())
 {
  if (file.isDirectory())
   printAllFiles(file);
  else
   System.out.println(file.getAbsolutePath());
 }
}

"Línea 8: obtenemos la lista de todos los archivos (y directorios) en el directorio dir".

"Líneas 10-11: si el archivo es en realidad un directorio, llamamos a printAllFiles nuevamente, pero esta vez con otro argumento: el subdirectorio".

"Línea 13: mostramos el nombre del archivo actual".

"Está bien. Creo que entiendo. Gracias, Bilaabo".