Articulo de referencia

Recursión anónima

En informática , la recursión anónima es aquella que no llama explícitamente a una función por su nombre. Esto puede hacerse de forma explícita, utilizando una función de orden ...

En informática , la recursión anónima es aquella que no llama explícitamente a una función por su nombre. Esto puede hacerse de forma explícita, utilizando una función de orden superior (pasando una función como argumento y llamándola), o de forma implícita, mediante características de reflexión que permiten acceder a ciertas funciones según el contexto actual, especialmente a "la función actual" o, en ocasiones, a "la función que llama a la función actual".

En la práctica de la programación, la recursión anónima se utiliza notablemente en JavaScript , que proporciona herramientas de reflexión para soportarla. Sin embargo, en la práctica general de la programación, esto se considera una mala práctica, y se sugiere en su lugar la recursión con funciones con nombre. La recursión anónima mediante el paso explícito de funciones como argumentos es posible en cualquier lenguaje que admita funciones como argumentos, aunque esto rara vez se usa en la práctica, ya que es más largo y menos claro que la recursión explícita por nombre.

En informática teórica, la recursión anónima es importante, ya que demuestra que se puede implementar la recursión sin necesidad de funciones con nombre. Esto es particularmente importante para el cálculo lambda , que posee funciones unarias anónimas, pero es capaz de calcular cualquier función recursiva. Esta recursión anónima se puede generar de forma genérica mediante combinadores de punto fijo .

Usar

La recursión anónima se utiliza principalmente para permitir la recursión de funciones anónimas , en particular cuando forman cierres o se utilizan como funciones de devolución de llamada , para evitar tener que vincular el nombre de la función.

La recursión anónima consiste principalmente en llamar a "la función actual", lo que resulta en una recursión directa . También es posible la recursión indirecta anónima , por ejemplo, llamando a "la función que realizó la llamada (la función anterior)" o, más raramente, ascendiendo en la pila de llamadas , lo que puede encadenarse para producir recursión mutua . La autorreferencia de "la función actual" es un equivalente funcional de la palabra clave " this " en la programación orientada a objetos , lo que permite referirse al contexto actual.

La recursión anónima también puede utilizarse para funciones con nombre, en lugar de llamarlas por su nombre, por ejemplo, para especificar que se está aplicando la recursión a la función actual, o para permitir que se cambie el nombre de la función sin necesidad de modificar el nombre donde se llama a sí misma. Sin embargo, como práctica habitual en programación, esto no se suele hacer.

Alternativas

Funciones con nombre

La alternativa habitual es usar funciones con nombre y recursión con nombre. Dada una función anónima, esto se puede hacer asignando un nombre a la función, como en las expresiones de función con nombre en JavaScript, o asignando la función a una variable y luego llamando a la variable, como en las sentencias de función en JavaScript. Dado que los lenguajes que permiten funciones anónimas generalmente permiten asignar estas funciones a variables (si no son funciones de primera clase), muchos lenguajes no proporcionan una forma de referirse a la función en sí y rechazan explícitamente la recursión anónima; ejemplos de ello son Go . [ 1 ]

Por ejemplo, en JavaScript la función factorial se puede definir mediante recursión anónima de la siguiente manera: [ 2 ]

[ 1 , 2 , 3 , 4 , 5 ]. mapa ( función ( n ) { retorno ( ! ( n > 1 )) ? 1 : argumentos . callee ( n - 1 ) * n ; });

Reescrito para usar una expresión de función con nombre produce:

[ 1 , 2 , 3 , 4 , 5 ]. map ( function factorial ( n ) { return ( ! ( n > 1 )) ? 1 : factorial ( n - 1 ) * n ; });

Pasar funciones como argumentos

Incluso sin mecanismos para referirse a la función actual o a la función que realiza la llamada, la recursión anónima es posible en un lenguaje que permite funciones como argumentos. Esto se logra añadiendo otro parámetro a la función recursiva básica y utilizando este parámetro como la función para la llamada recursiva. Esto crea una función de orden superior, y al pasar esta función superior se permite la recursión anónima dentro de la función recursiva propiamente dicha. Esto se puede hacer de forma completamente anónima aplicando un combinador de punto fijo a esta función de orden superior. Esto es principalmente de interés académico, en particular para demostrar que el cálculo lambda tiene recursión, ya que la expresión resultante es significativamente más compleja que la función recursiva original con nombre. Por el contrario, el uso de combinadores de punto fijo puede denominarse genéricamente "recursión anónima", ya que este es un uso notable de ellos, aunque tienen otras aplicaciones. [ 3 ] [ 4 ]

Esto se ilustra a continuación usando Python . Primero, una recursión con nombre estándar:

def fact ( n ): if n == 0 : return 1 return n * fact ( n - 1 )

Utilizar una función de orden superior para que la función de nivel superior recurse de forma anónima sobre un argumento, pero aún necesitando la función recursiva estándar como argumento:

def fact0 ( n0 ): if n0 == 0 : return 1 return n0 * fact0 ( n0 - 1 ) fact1 = lambda f , n1 : 1 if n1 == 0 else n1 * f ( n1 - 1 ) fact = lambda n : fact1 ( fact0 , n )

Podemos eliminar la función recursiva estándar pasando el argumento de la función a la llamada:

fact1 = lambda f , n1 : 1 si n1 == 0 sino n1 * f ( f , n1 - 1 ) fact = lambda n : fact1 ( fact1 , n )

La segunda línea puede ser reemplazada por una función genérica de orden superior llamada combinador :

F = lambda f : ( lambda x : f ( f , x )) fact1 = lambda f , n1 : 1 if n1 == 0 else n1 * f ( f , n1 - 1 ) fact = F ( fact1 )

Escrito anónimamente: [ 5 ]

( lambda f : ( lambda x : f ( f , x ))) \ ( lambda g , n1 : 1 if n1 == 0 else n1 * g ( g , n1 - 1 ))

En el cálculo lambda , que solo utiliza funciones de una sola variable, esto se puede hacer mediante el combinador Y. Primero , haga que la función de orden superior de dos variables sea una función de una sola variable, que devuelva directamente una función, mediante currificación :

hecho1 = lambda f : ( lambda n1 : 1 si n1 == 0 sino n1 * f ( f )( n1 - 1 )) hecho = hecho1 ( hecho1 )

Aquí hay dos operaciones de "aplicar una función de orden superior a sí misma": f(f)en la primera línea y fact1(fact1)en la segunda. Al factorizar la segunda aplicación doble en un combinador , obtenemos:

C = lambda x : x ( x ) fact1 = lambda f : ( lambda n1 : 1 if n1 == 0 else n1 * f ( f )( n1 - 1 )) fact = C ( fact1 )

Al factorizar la otra doble aplicación se obtiene:

C = lambda x : x ( x ) D = lambda f : ( lambda x : f ( lambda v : x ( x )( v ))) fact1 = lambda g : ( lambda n1 : 1 if n1 == 0 else n1 * g ( n1 - 1 )) fact = C ( D ( fact1 ))

La combinación de los dos combinadores en uno solo da como resultado el combinador Y :

C = lambda x : x ( x ) D = lambda f : ( lambda x : f ( lambda v : x ( x )( v ))) Y = lambda y : C ( D ( y )) fact1 = lambda g : ( lambda n1 : 1 if n1 == 0 else n1 * g ( n1 - 1 )) fact = Y ( fact1 )

Al expandir el combinador Y se obtiene:

Y = lambda f : ( lambda x : f ( lambda v : x ( x )( v ))) \ ( lambda x : f ( lambda v : x ( x )( v ))) fact1 = lambda g : ( lambda n1 : 1 if n1 == 0 else n1 * g ( n1 - 1 )) fact = Y ( fact1 )

La combinación de estos elementos produce una definición recursiva del factorial en el cálculo lambda (funciones anónimas de una sola variable): [ 6 ]

( lambda f : ( lambda x : f ( lambda v : x ( x )( v ))) ( lambda x : f ( lambda v : x ( x )( v )))) \ ( lambda g : ( lambda n1 : 1 if n1 == 0 else n1 * g ( n1 - 1 )))

Ejemplos

APL

En APL , la función dinámica actual es accesible a través de . Esto permite la recursión anónima, como en esta implementación del factorial:

{ 0 = ⍵: 1 × - 1 } 5 120 { 0 = ⍵: 1 × - 1 } ¨ 10 ⍝ aplicado a cada elemento de 0 a 9 1 1 2 6 24 120 720 5040 40320 362880

JavaScript

En JavaScript , la función actual es accesible a través de arguments.callee, mientras que la función que la llama es accesible a través de arguments.caller. Esto permite la recursión anónima, como en esta implementación del factorial: [ 2 ]

[ 1 , 2 , 3 , 4 , 5 ]. mapa ( función ( n ) { retorno ( ! ( n > 1 )) ? 1 : argumentos . callee ( n - 1 ) * n ; });

Perl

A partir de Perl 5.16, se puede acceder a la subrutina actual mediante el __SUB__token, que devuelve una referencia a la subrutina actual, o undefdesde fuera de una subrutina. [ 7 ] Esto permite la recursión anónima, como en la siguiente implementación del factorial:

#!/usr/bin/env perl use feature ":5.16" ;print sub { my $x = shift ; $x > 0 ? $x * __SUB__ -> ( $x - 1 ) : 1 ; } -> ( 5 ), "\n" ;

R

En R , la función actual se puede llamar usando Recall. Por ejemplo,

aplicar ( 0 : 5 , función ( n ) { si ( n == 0 ) devolver ( 1 ) n * Recordar ( n - 1 ) })

Sin embargo, no funcionará si se pasa como argumento a otra función, por ejemplo lapply, dentro de la definición de la función anónima. En este caso, sys.function(0)se puede usar. [ 8 ] Por ejemplo, el siguiente código eleva al cuadrado una lista recursivamente:

( función ( x ) { si ( es.lista ( x )) { aplicar ( x , sys.función ( 0 )) } de lo contrario { x ^ 2 } })( lista ( lista ( 1 , 2 , 3 ), lista ( 4 , 5 )))

Referencias

  1. Problema 226: Es imposible hacer recursión en una función anónima en Go sin soluciones alternativas.
  2. 1 2 respuesta de olliej, 25 de octubre de 2008 a " ¿Por qué se descontinuó la propiedad arguments.callee.caller en JavaScript? ", StackOverflow
  3. Esta terminología parece ser en gran parte folclore , pero aparece en lo siguiente:
    • Trey Nash, Accelerated C# 2008 , Apress, 2007, ISBN 1-59059-873-3, págs. 462-463. Basado sustancialmente en el blog de Wes Dyer (véase el siguiente punto).
    • El artículo de Wes Dyer, "Recursión anónima en C#" , del 2 de febrero de 2007, contiene un ejemplo sustancialmente similar al que se encuentra en el libro mencionado anteriormente, pero acompañado de una explicación más detallada.
  4. El método If Works : Derivación del combinador Y , 10 de enero de 2008
  5. Respuesta de Hugo Walter a " ¿Puede una función lambda llamarse a sí misma recursivamente en Python? "
  6. Respuesta de Nux a " ¿Puede una función lambda llamarse a sí misma recursivamente en Python? "
  7. Perldoc, " La característica 'current_sub' ", característica de perldoc
  8. Respuesta de agstudy a Obtener la función que se está llamando actualmente para escribir una función recursiva anónima en StackOverflow