Digamos que eu tenho uma função recursiva básica:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
Como eu poderia fazer isso se eu tivesse uma função anônima como ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Eu gostaria de uma maneira de chamar a função que chamou essa função ... Eu já vi scripts em algum lugar (não me lembro onde) que podem dizer o nome de uma função chamada, mas não consigo me lembrar de nenhum essa informação agora.
javascript
recursion
scope
anonymous-function
Incógnito
fonte
fonte
arguments.callee
, e essa função não faz nada de útil. Eu estava procurando o Y combinator:P
. Porra, esse material será não obter útil ...Respostas:
Você pode dar um nome à função, mesmo quando estiver criando a função como um valor e não como uma declaração de "declaração de função". Em outras palavras:
é uma função recursiva de empilhamento de pilhas. Agora, dito isso, você
provavelmentenão deseja fazer isso em geral, porque existem alguns problemas estranhos com várias implementações de Javascript. ( nota - esse é um comentário bastante antigo; alguns / muitos / todos os problemas descritos na postagem do blog de Kangax podem ser corrigidos em navegadores mais modernos.)Quando você dá um nome assim, o nome não é visível fora da função (bem, não deveria ser; essa é uma das estranhezas). É como "letrec" no Lisp.
Quanto a
arguments.callee
isso, isso não é permitido no modo "estrito" e geralmente é considerado uma coisa ruim, porque dificulta algumas otimizações. Também é muito mais lento do que se poderia esperar.editar - Se você deseja ter o efeito de uma função "anônima" que pode se chamar, você pode fazer algo assim (supondo que você esteja passando a função como retorno de chamada ou algo assim):
O que isso faz é definir uma função com uma declaração de função agradável, segura e não quebrada no IE , criando uma função local cujo nome não poluirá o espaço para nome global. A função wrapper (verdadeiramente anônima) apenas retorna essa função local.
fonte
(() => { call_recursively_self_here() })()
e chamar-se recursivamente, certo? Eu devo dar um nome.As pessoas falaram sobre o combinador Y nos comentários, mas ninguém o escreveu como resposta.
O combinador Y pode ser definido em javascript da seguinte forma: (graças a steamer25 pelo link)
E quando você deseja passar sua função anônima:
O mais importante a ser observado sobre esta solução é que você não deve usá-la.
fonte
Combinador em U
Ao passar uma função para si mesma como argumento, uma função pode se repetir usando seu parâmetro em vez de seu nome! Portanto, a função atribuída
U
deve ter pelo menos um parâmetro que se vincule à função (ela mesma).No exemplo abaixo, não temos condição de saída, portanto, executaremos um loop indefinidamente até que ocorra um estouro de pilha
Podemos parar a recursão infinita usando uma variedade de técnicas. Aqui, escreverei nossa função anônima para retornar outra função anônima que está aguardando uma entrada; neste caso, algum número. Quando um número é fornecido, se for maior que 0, continuaremos recorrendo, caso contrário, retornará 0.
O que não é imediatamente aparente aqui é que nossa função, quando aplicada pela primeira vez a si mesma usando o
U
combinador, retorna uma função aguardando a primeira entrada. Se dermos um nome a isso, podemos efetivamente construir funções recursivas usando lambdas (funções anônimas)Só que isso não é recursão direta - uma função que se chama usando seu próprio nome. Nossa definição de
countDown
não se refere a si mesma dentro de seu corpo e ainda é possível recursãoComo remover a auto-referência de uma função existente usando o combinador U
Aqui, mostrarei como pegar uma função recursiva que usa uma referência para si mesma e alterá-la para uma função que emprega o combinador U no lugar da auto-referência
Agora, usando o combinador U para substituir a referência interna para
factorial
O padrão básico de substituição é esse. Faça uma anotação mental, usaremos uma estratégia semelhante na próxima seção
Combinador Y
Na seção anterior, vimos como transformar a recursão de auto-referência em uma função recursiva que não depende de uma função nomeada usando o combinador U. Há um certo aborrecimento em ter que se lembrar de sempre passar a função para si mesma como o primeiro argumento. Bem, o combinador Y se baseia no combinador U e remove esse bit tedioso. Isso é bom porque remover / reduzir a complexidade é a principal razão pela qual fazemos funções
Primeiro, vamos derivar nosso próprio combinador Y
Agora veremos como seu uso se compara ao combinador em U. Observe, para repetir, em vez de
U (f)
simplesmente chamarmosf ()
Agora vou demonstrar o
countDown
programa usandoY
- você verá que os programas são quase idênticos, mas o combinador Y mantém as coisas um pouco mais limpasE agora vamos ver
factorial
tambémComo você pode ver,
f
torna-se o mecanismo de recursão em si. Para repetir, chamamos isso de uma função comum. Podemos chamá-lo várias vezes com argumentos diferentes e o resultado ainda estará correto. E como é um parâmetro de função comum, podemos nomear o que quisermos, comorecur
abaixo -Combinador U e Y com mais de 1 parâmetro
Nos exemplos acima, vimos como podemos fazer um loop e passar um argumento para acompanhar o "estado" de nossa computação. Mas e se precisarmos acompanhar o estado adicional?
Nós poderia usar os dados compostos como um Array ou algo assim ...
Mas isso é ruim porque está expondo o estado interno (contadores
a
eb
). Seria bom se pudéssemos ligarfibonacci (7)
para obter a resposta que queremos.Usando o que sabemos sobre funções com caril (sequências de funções unárias (1 parâmetro)), podemos alcançar nosso objetivo facilmente sem precisar modificar nossa definição
Y
ou confiar em dados compostos ou recursos avançados de linguagem.Veja
fibonacci
atentamente a definição de abaixo. Estamos aplicando imediatamente0
e1
quais são obrigadosa
eb
respectivamente. Agora, fibonacci está simplesmente aguardando o último argumento a ser fornecido, que será vinculadox
. Quando recessamos, devemos ligarf (a) (b) (x)
(nãof (a,b,x)
) porque nossa função está na forma de caril.Esse tipo de padrão pode ser útil para definir todos os tipos de funções. Abaixo veremos mais duas funções definidas usando o
Y
combinator (range
ereduce
) e um derivado dereduce
,map
.É TUDO ANÔNIMO OMG
Como estamos trabalhando com funções puras aqui, podemos substituir qualquer função nomeada por sua definição. Veja o que acontece quando pegamos fibonacci e substituímos funções nomeadas por suas expressões
E aí está -
fibonacci (7)
calculado recursivamente usando nada além de funções anônimasfonte
Pode ser mais simples usar um "objeto anônimo":
Seu espaço global é completamente poluído. É bem direto. E você pode facilmente tirar proveito do estado não global do objeto.
Você também pode usar os métodos de objeto ES6 para tornar a sintaxe mais concisa.
fonte
Eu não faria isso como uma função embutida. Ele está empurrando contra as fronteiras do bom gosto e realmente não recebe nada.
Se você realmente precisa, há
arguments.callee
como na resposta de Fabrizio. No entanto, isso geralmente é considerado desaconselhável e não é permitido no 'modo estrito' do ECMAScript Fifth Edition. Embora o ECMA 3 e o modo não estrito não estejam desaparecendo, o trabalho no modo estrito promete mais otimizações de idioma possíveis.Pode-se também usar uma função embutida nomeada:
No entanto, expressões com funções inline nomeadas também são evitadas, pois o JScript do IE faz algumas coisas ruins a elas. No exemplo acima,
foo
polui incorretamente o escopo pai no IE, e o paifoo
é uma instância separada dafoo
vista por dentrofoo
.Qual é o objetivo de colocar isso em uma função anônima embutida? Se você apenas deseja evitar poluir o escopo pai, é claro que pode ocultar seu primeiro exemplo dentro de outra função de auto-chamada-anônima (espaço de nome). Você realmente precisa criar uma nova cópia de
nothing
cada vez em torno da recursão? Você pode estar melhor com um espaço para nome contendo duas funções simples recursivas.fonte
"pushing against the boundaries of good taste"
- (bem, e as boas informações).recur_foo
colide com uma função no escopo pai (ou fique doente -usava) .fonte
arguments.callee
: não é permitido no modo estrito e no ES5.Você poderia fazer algo como:
ou no seu caso:
fonte
recur
primeiro com umavar
declaração. Não sei se isso infringe as regras da pergunta, mas como você a possui agora, sem avar
declaração, você receberá um erro no modo estrito do ECMAScript 5.var
palavra - chave, mas uma vez que testei esse código, ele estava gerando erros, já que você não pode realmente declarar uma variável dentro de um bloco auto-invocador, e minha abordagem se baseia na declaração automática de uma variável indefinida e, portanto, no @ Pointy's solução é mais correta. Mas eu ainda votou Fabrizio Calderan resposta embora;)(var recur = function() {...})();
não funcionará, pois agora é uma instrução, e não uma expressão de atribuição (que retorna o valor atribuído). Eu estava sugerindovar recur; (recur = function() {...})();
.Quando você declara uma função anônima como esta:
É considerada uma expressão de função e possui um nome opcional (que você pode usar para chamá-la de dentro de si mesma. Mas, por ser uma expressão de função (e não uma declaração), permanece anônima (mas tem um nome que você pode chamar). esta função pode se chamar:
fonte
foo
isso não é declarado no contexto atual, mas isso é mais ou menos irrelevante. Uma função com um nome ainda é uma função nomeada - não anônima.Por que não passar a função para a própria função?
fonte
Em certas situações, você precisa confiar em funções anônimas. Dada é uma
map
função recursiva :Observe que
map
não deve modificar a estrutura da matriz. Portanto, o acumuladoracc
não precisa ser exposto. Podemos envolvermap
outra função, por exemplo:Mas esta solução é bastante detalhada. Vamos usar o
U
combinador subestimado :Conciso, não é?
U
é simples, mas tem a desvantagem de que a chamada recursiva fica um pouco ofuscada:sum(...)
torna - seh(h)(...)
- isso é tudo.fonte
Não tenho certeza se a resposta ainda é necessária, mas isso também pode ser feito usando delegados criados usando function.bind:
Isso não envolve funções nomeadas ou argumentos.
fonte
Como bobince escreveu, basta nomear sua função.
Mas, acho que você também deseja passar um valor inicial e interromper sua função eventualmente!
Exemplo de trabalho jsFiddle (usa dados + = dados por diversão)
fonte
However named inline function expressions are also best avoided.
. Mas o OP perde o ponto também ... :)eu precisava (ou melhor, queria) de uma função anônima de uma linha para subir um objeto que cria uma cadeia de caracteres e a manipulava da seguinte maneira:
que produz uma string como 'Root: foo: bar: baz: ...'
fonte
Com o ES2015, podemos brincar um pouco com a sintaxe e abusar dos parâmetros e thunks padrão. Estes últimos são apenas funções sem argumentos:
Observe que este
f
é um parâmetro com a função anônima(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
como valor padrão. Quandof
é invocado porapplyT
esta invocação deve ocorrer sem argumentos, para que o valor padrão seja usado. O valor padrão é uma função e, portanto,f
é uma função nomeada, que pode se chamar recursivamente.fonte
Outra resposta que não envolve função nomeada ou argumentos.
fonte
Este é um retrabalho da resposta do jforjs com nomes diferentes e uma entrada ligeiramente modificada.
Não houve necessidade de desenrolar a primeira recursão. A função que recebe a si mesma como referência remete ao lodo primordial da OOP.
fonte
Esta é uma versão da resposta do @ zem com funções de seta.
Você pode usar o
U
ou oY
combinador. O combinador Y é o mais simples de usar.U
combinator, com isso você deve continuar passando a função:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
combinator, com isso você não precisa continuar passando a função:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
fonte
Outra solução do combinador Y, usando o link do código rosetta (acho que alguém já mencionou o link em algum lugar no stackOverflow.
As setas são para funções anônimas mais legíveis para mim:
fonte
Isso pode não funcionar em qualquer lugar, mas você pode usar
arguments.callee
para se referir à função atual.Assim, o fatorial poderia ser feito assim:
fonte