Digamos que tengo una función recursiva básica:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
¿Cómo podría hacer esto si tengo una función anónima como ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Me gustaría una forma de llamar a la función que llamó a esta función ... He visto scripts en alguna parte (no recuerdo dónde) que pueden indicarle el nombre de una función llamada, pero no recuerdo ninguno de esa información ahora mismo.
javascript
recursion
scope
anonymous-function
Incógnito
fuente
fuente
arguments.callee
exista, y esta functnio no hace nada útil. Estaba buscando Y Combinator:P
. Maldita sea, esas cosas nunca serán útiles ...Respuestas:
Usted puede dar a la función de un nombre, incluso cuando se va a crear la función como un valor y no una declaración "declaración de la función". En otras palabras:
es una función recursiva acumulativa. Ahora bien, dicho esto,
probablementeno quieras hacer esto en general porque hay algunos problemas extraños con varias implementaciones de Javascript. ( nota : es un comentario bastante antiguo; algunos / muchos / todos los problemas descritos en la publicación del blog de Kangax pueden solucionarse en navegadores más modernos).Cuando le da un nombre así, el nombre no es visible fuera de la función (bueno, no se supone que lo sea; esa es una de las rarezas). Es como "letrec" en Lisp.
En cuanto a
arguments.callee
, eso no está permitido en el modo "estricto" y generalmente se considera algo malo, porque dificulta algunas optimizaciones. También es mucho más lento de lo que cabría esperar.editar : si desea tener el efecto de una función "anónima" que puede llamarse a sí misma, puede hacer algo como esto (asumiendo que está pasando la función como una devolución de llamada o algo así):
Lo que hace es definir una función con una declaración de declaración de función agradable, segura y no rota en IE , creando una función local cuyo nombre no contaminará el espacio de nombres global. La función contenedora (verdaderamente anónima) simplemente devuelve esa función local.
fuente
(() => { call_recursively_self_here() })()
y llamarse a sí mismo de forma recursiva, ¿verdad? Debo darle un nombre.La gente habló sobre el combinador Y en los comentarios, pero nadie lo escribió como respuesta.
El combinador Y se puede definir en javascript de la siguiente manera: (gracias a steamer25 por el enlace)
Y cuando quieras pasar tu función anónima:
Lo más importante a tener en cuenta sobre esta solución es que no debe usarla.
fuente
Combinador U
Al pasar una función a sí misma como argumento, una función puede repetirse usando su parámetro en lugar de su nombre. Entonces la función dada a
U
debe tener al menos un parámetro que se unirá a la función (en sí).En el siguiente ejemplo, no tenemos una condición de salida, por lo que solo realizaremos un bucle indefinido hasta que ocurra un desbordamiento de pila.
Podemos detener la recursividad infinita usando una variedad de técnicas. Aquí, escribiré nuestra función anónima para devolver otra función anónima que está esperando una entrada; en este caso, algún número. Cuando se proporciona un número, si es mayor que 0, continuaremos recurriendo, de lo contrario devolveremos 0.
Lo que no es evidente de inmediato aquí es que nuestra función, cuando se aplica por primera vez a sí misma utilizando el
U
combinador, devuelve una función que espera la primera entrada. Si le damos un nombre a esto, podemos construir de manera efectiva funciones recursivas usando lambdas (funciones anónimas)Solo que esto no es recursividad directa , una función que se llama a sí misma usando su propio nombre. Nuestra definición de
countDown
no hace referencia a sí misma dentro de su cuerpo y aún así la recursividad es posibleCómo eliminar la autorreferencia de una función existente usando el combinador U
Aquí le mostraré cómo tomar una función recursiva que usa una referencia a sí misma y cambiarla a una función que emplea el combinador U en lugar de la autorreferencia.
Ahora usando el combinador U para reemplazar la referencia interna a
factorial
El patrón de reemplazo básico es este. Tome nota mentalmente, usaremos una estrategia similar en la siguiente sección
Combinador Y
En la sección anterior vimos cómo transformar la recursividad de autorreferencia en una función recursiva que no depende de una función nombrada usando el combinador U. Hay un poco de molestia aunque tener que recordar pasar siempre la función a sí misma como primer argumento. Bueno, el combinador Y se basa en el combinador U y elimina esa parte tediosa. Esto es bueno porque eliminar / reducir la complejidad es la razón principal por la que hacemos funciones
Primero, derivemos nuestro propio combinador Y
Ahora veremos cómo se compara su uso con el combinador U. Observe, para recurrir, en lugar de
U (f)
simplemente podemos llamarf ()
Ahora demostraré el
countDown
uso del programaY
: verá que los programas son casi idénticos, pero el combinador Y mantiene las cosas un poco más limpiasY ahora veremos
factorial
tambiénComo puede ver, se
f
convierte en el propio mecanismo de recursividad. Para recurrir, lo llamamos como una función ordinaria. Podemos llamarlo varias veces con diferentes argumentos y el resultado seguirá siendo correcto. Y dado que es un parámetro de función ordinario, podemos nombrarlo como queramos, como arecur
continuación:Combinador U e Y con más de 1 parámetro
En los ejemplos anteriores, vimos cómo podemos hacer un bucle y pasar un argumento para realizar un seguimiento del "estado" de nuestro cálculo. Pero, ¿y si necesitamos realizar un seguimiento del estado adicional?
Nosotros podríamos utilizar los datos de compuestos como una matriz o algo ...
Pero esto es malo porque expone el estado interno (contadores
a
yb
). Sería bueno si pudiéramos llamarfibonacci (7)
para obtener la respuesta que queremos.Usando lo que sabemos sobre funciones curry (secuencias de funciones unarias (1 parámetro)), podemos lograr nuestro objetivo fácilmente sin tener que modificar nuestra definición
Y
o depender de datos compuestos o características avanzadas del lenguaje.Mira la definición de de
fibonacci
cerca a continuación. Estamos solicitando de inmediato0
y1
que están vinculados aa
yb
respectivamente. Ahora, fibonacci simplemente está esperando que se proporcione el último argumento al que se vincularáx
. Cuando recurrimos, debemos llamarf (a) (b) (x)
(nof (a,b,x)
) porque nuestra función está en forma de curry.Este tipo de patrón puede resultar útil para definir todo tipo de funciones. A continuación veremos dos funciones más definidas mediante el
Y
combinador (range
yreduce
) y un derivado dereduce
,map
.TODO ES ANÓNIMO OMG
Como aquí estamos trabajando con funciones puras, podemos sustituir su definición por cualquier función nombrada. Observe lo que sucede cuando tomamos fibonacci y reemplazamos funciones nombradas con sus expresiones
Y ahí lo tiene:
fibonacci (7)
calculado de forma recursiva utilizando nada más que funciones anónimasfuente
Puede ser más sencillo utilizar un "objeto anónimo" en su lugar:
Su espacio global está completamente impoluto. Es bastante sencillo. Y puede aprovechar fácilmente el estado no global del objeto.
También puede utilizar métodos de objeto ES6 para hacer la sintaxis más concisa.
fuente
No haría esto como una función en línea. Es ir más allá de los límites del buen gusto y realmente no te ofrece nada.
Si realmente debe hacerlo, hay
arguments.callee
como en la respuesta de Fabrizio. Sin embargo, esto generalmente se considera desaconsejable y no está permitido en el 'modo estricto' de ECMAScript Quinta edición. Aunque ECMA 3 y el modo no estricto no desaparecerán, trabajar en modo estricto promete más optimizaciones de lenguaje posibles.También se puede usar una función en línea con nombre:
Sin embargo, es mejor evitar las expresiones de función en línea con nombre, ya que JScript de IE les hace algunas cosas malas. En el ejemplo anterior
foo
contamina incorrectamente el alcance principal en IE, y el padrefoo
es una instancia separada delfoo
visto dentrofoo
.¿Cuál es el propósito de poner esto en una función anónima en línea? Si solo desea evitar contaminar el ámbito principal, por supuesto, puede ocultar su primer ejemplo dentro de otra función auto-llamada-anónima (espacio de nombres). ¿Realmente necesitas crear una nueva copia de
nothing
cada vez que se realiza la recursividad? Es posible que esté mejor con un espacio de nombres que contenga dos funciones recursivas recursivas simples.fuente
"pushing against the boundaries of good taste"
- (bueno, y la buena información).recur_foo
colisione con una función en el alcance principal (o que esté enfermo -usado) .fuente
arguments.callee
: no está permitido en modo estricto y en ES5.Podrías hacer algo como:
o en tu caso:
fuente
recur
primero con unavar
declaración. No sé si eso rompe las reglas de la pregunta, pero como lo tiene ahora, sin lavar
declaración obtendrá un error en el modo estricto de ECMAScript 5.var
palabra clave, pero una vez que probé este código, arrojaba errores, ya que realmente no se puede declarar una variable dentro de un bloque autoinvocante, y mi enfoque se basa en la declaración automática de una variable indefinida y, por lo tanto, @ Pointy's La solución es más correcta. Pero todavía voté por la respuesta de Fabrizio Calderan;)(var recur = function() {...})();
no funcionará ya que ahora es una declaración en lugar de una expresión de asignación (que devuelve el valor asignado). En cambio, estaba sugiriendovar recur; (recur = function() {...})();
.Cuando declaras una función anónima como esta:
Se considera una expresión de función y tiene un nombre opcional (que puede usar para llamarla desde dentro de sí misma. Pero debido a que es una expresión de función (y no una declaración) permanece anónima (pero tiene un nombre que puede llamar). esta función puede llamarse a sí misma:
fuente
foo
no se declara dentro del contexto actual, pero eso es más o menos irrelevante. Una función con un nombre sigue siendo una función con nombre, no anónima.¿Por qué no pasar la función a la propia función?
fuente
En determinadas situaciones, debe confiar en funciones anónimas. Dado es una
map
función recursiva :Tenga en cuenta que
map
no debe modificar la estructura de la matriz. Por tanto,acc
no es necesario exponer el acumulador . Podemos ajustarnosmap
a otra función, por ejemplo:Pero esta solución es bastante detallada. Usemos el
U
combinador subestimado :Conciso, ¿no?
U
es muy simple pero tiene la desventaja de que la llamada recursiva se ofusca un poco: sesum(...)
convierte enh(h)(...)
- eso es todo.fuente
No estoy seguro de si aún se requiere la respuesta, pero esto también se puede hacer usando delegados creados usando function.bind:
Esto no implica funciones o argumentos con nombre.
fuente
Como escribió bobince, simplemente nombre su función.
Pero, supongo que también desea pasar un valor inicial y detener su función eventualmente.
ejemplo de trabajo de jsFiddle (usa datos + = datos por diversión)
fuente
However named inline function expressions are also best avoided.
. Pero el OP también pierde el punto ... :)Necesitaba (o mejor dicho, quería) una función anónima de una sola línea para subir un objeto construyendo una cadena, y lo manejé así:
que produce una cadena como 'Root: foo: bar: baz: ...'
fuente
Con ES2015 podemos jugar un poco con la sintaxis y abusar de los parámetros y procesadores predeterminados. Estos últimos son solo funciones sin ningún argumento:
Tenga en cuenta que
f
es un parámetro con la función anónima(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
como valor predeterminado. Cuandof
es invocado porapplyT
esta invocación debe tener lugar sin argumentos, por lo que se utiliza el valor predeterminado. El valor predeterminado es una función y, porf
lo tanto, es una función con nombre, que puede llamarse a sí misma de forma recursiva.fuente
Otra respuesta que no involucra una función o argumentos nombrados.
fuente
Esta es una reelaboración de la respuesta de jforjs con diferentes nombres y una entrada ligeramente modificada.
No fue necesario desenrollar la primera recursividad. La función que se recibe a sí misma como referencia se remonta al exudado primordial de la POO.
fuente
Esta es una versión de la respuesta de @ zem con funciones de flecha.
Puede utilizar el
U
o elY
combinador . El combinador Y es el más simple de usar.U
combinador, con esto tienes que seguir pasando la función:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
combinador, con esto no tienes que seguir pasando la función:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
fuente
Otra solución de combinador en Y, usando el enlace de código rosetta (creo que alguien mencionó anteriormente el enlace en algún lugar de stackOverflow.
Las flechas son para funciones anónimas más legibles para mí:
fuente
Es posible que esto no funcione en todas partes, pero puede usarlo
arguments.callee
para consultar la función actual.Entonces, factorial podría hacerse así:
fuente