javascript: função anônima recursiva?

120

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.

Incógnito
fonte
Existe uma razão para você precisar disso ou está apenas curioso? Parece-me que seria mais claro para simplesmente dar-lhe um nome ...
rfunduk
1
@ thenduks: Pela mesma razão pela qual alguém usaria uma função anônima. Só que às vezes é necessária recursão.
Poke
5
É uma pena 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 ...
Kobi
1
Sim, como Kobi vinculado, use um combinador de ponto fixo como Y para executar funções recursivas anônimas sem argumentos.
usar o seguinte
1
Consulte w3future.com/weblog/stories/2002/02/22/… para obter um exemplo do combinador Y em JS.
usar o seguinte

Respostas:

145

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:

(function foo() { foo(); })();

é uma função recursiva de empilhamento de pilhas. Agora, dito isso, você provavelmente nã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.calleeisso, 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):

asyncThingWithCallback(params, (function() {
  function recursive() {
    if (timeToStop())
      return whatever();
    recursive(moreWork);
  }
  return recursive;
})());

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.

Pontudo
fonte
Podemos evitar poluir o espaço de nomes global de outra maneira com o ES5 sctrict (ainda não li o ES5 ainda)?
Incognito
@ pointy você pode, por favor, olhar para esta missão. stackoverflow.com/questions/27473450/…
Gladson Robinson
Eu acho que não é possível usar (() => { call_recursively_self_here() })()e chamar-se recursivamente, certo? Eu devo dar um nome.
Qwerty 08/02
1
@ Qwerty bem, você poderia fazer algo como o último exemplo na minha resposta. Ligue a função de seta a uma variável local em uma função de wrapper para que sua função de seta possa se referir a si mesma com o nome da variável. O wrapper retornaria a variável (que se refere à função de seta).
Pointy
1
@Pointy talvez alguns hackers vão encontrar aplicação;)
Kamil Kiełczewski
31

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)

var Y = function (gen) {
  return (function(f) {
    return f(f);
  }(function(f) {
    return gen(function() {
      return f(f).apply(null, arguments);
    });
  }));
}

E quando você deseja passar sua função anônima:

(Y(function(recur) {
  return function(data) {
    data = data+1;
    var nothing = function() {
      recur(data);
    }
    nothing();
  }
})());

O mais importante a ser observado sobre esta solução é que você não deve usá-la.

zem
fonte
16
"O mais importante a ser observado sobre esta solução é que você não deve usá-la." Por quê?
precisa saber é o seguinte
7
Não será rápido. É realmente feio de usar (embora conceitualmente bonito!). Você evita ter que dar à sua função um tag ou nome de variável (e não vejo por que isso seria uma preocupação), mas você ainda atribui um nome como parâmetro para a função externa passada para Y. Portanto, você não ganhar qualquer coisa passando por todo esse problema.
Zem
Não se esqueça de mencionar que esta função não é segura para a pilha. Fazer um loop apenas algumas milhares de vezes resultará em um estouro de pilha.
Obrigado
Oi, eu proporia uma modificação ligeiramente "mais limpa", pois .apply (null, argumentos) me parece feio: var Y = função (gen) {return (função (f) {return f (f);} (function (f) {return gen (função (x) {return f (f) (x);});})); } Ou equivalente ((função (x) {retorno y} é igual a (x => y)))) usando notação de seta (código js válido): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer
23

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 Udeve 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

const U = f => f (f) // call function f with itself as an argument

U (f => (console.log ('stack overflow imminent!'), U (f)))

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.

const log = x => (console.log (x), x)

const U = f => f (f)

// when our function is applied to itself, we get the inner function back
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
// returns: (x => x > 0 ? U (f) (log (x - 1)) : 0)
// where f is a reference to our outer function

// watch when we apply an argument to this function, eg 5
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0) (5)
// 4 3 2 1 0

O que não é imediatamente aparente aqui é que nossa função, quando aplicada pela primeira vez a si mesma usando o Ucombinador, 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)

const log = x => (console.log (x), x)

const U = f => f (f)

const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

Só que isso não é recursão direta - uma função que se chama usando seu próprio nome. Nossa definição de countDownnão se refere a si mesma dentro de seu corpo e ainda é possível recursão

// direct recursion references itself by name
const loop = (params) => {
  if (condition)
    return someValue
  else
    // loop references itself to recur...
    return loop (adjustedParams)
}

// U combinator does not need a named reference
// no reference to `countDown` inside countDown's definition
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

Como 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

const factorial = x =>
  x === 0 ? 1 : x * factorial (x - 1)
  
console.log (factorial (5)) // 120

Agora, usando o combinador U para substituir a referência interna para factorial

const U = f => f (f)

const factorial = U (f => x =>
  x === 0 ? 1 : x * U (f) (x - 1))

console.log (factorial (5)) // 120

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

// self reference recursion
const foo =         x => ...   foo (nextX) ...

// remove self reference with U combinator
const foo = U (f => x => ... U (f) (nextX) ...)

Combinador Y

relacionados: os combinadores U e Y explicados usando uma analogia de espelho

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

// standard definition
const Y = f => f (Y (f))

// prevent immediate infinite recursion in applicative order language (JS)
const Y = f => f (x => Y (f) (x))

// remove reference to self using U combinator
const Y = U (h => f => f (x => U (h) (f) (x)))

Agora veremos como seu uso se compara ao combinador em U. Observe, para repetir, em vez de U (f)simplesmente chamarmosf ()

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

Y (f => (console.log ('stack overflow imminent!'),  f ()))

Agora vou demonstrar o countDownprograma usando Y- você verá que os programas são quase idênticos, mas o combinador Y mantém as coisas um pouco mais limpas

const log = x => (console.log (x), x)

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const countDown = Y (f => x => x > 0 ? f (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

E agora vamos ver factorialtambém

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const factorial = Y (f => x =>
  x === 0 ? 1 :  x * f (x - 1))

console.log (factorial (5)) // 120

Como você pode ver, ftorna-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, como recurabaixo -

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (recur => n =>
  n < 2 ? n : recur (n - 1) +  (n - 2))

console.log (fibonacci (10)) // 55


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 ...

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => ([a, b, x]) =>
  x === 0 ? a : f ([b, a + b, x - 1]))

// starting with 0 and 1, generate the 7th number in the sequence
console.log (fibonacci ([0, 1, 7])) 
// 0 1 1 2 3 5 8 13

Mas isso é ruim porque está expondo o estado interno (contadores ae b). Seria bom se pudéssemos ligar fibonacci (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 You confiar em dados compostos ou recursos avançados de linguagem.

Veja fibonacciatentamente a definição de abaixo. Estamos aplicando imediatamente 0e 1quais são obrigados ae brespectivamente. Agora, fibonacci está simplesmente aguardando o último argumento a ser fornecido, que será vinculado x. Quando recessamos, devemos ligar f (a) (b) (x)(não f (a,b,x)) porque nossa função está na forma de caril.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => a => b => x =>
  x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)

console.log (fibonacci (7)) 
// 0 1 1 2 3 5 8 13


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 Ycombinator ( rangee reduce) e um derivado de reduce, map.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const range = Y (f => acc => min => max =>
  min > max ? acc : f ([...acc, min]) (min + 1) (max)) ([])

const reduce = Y (f => g => y => ([x,...xs]) =>
  x === undefined ? y : f (g) (g (y) (x)) (xs))
  
const map = f =>
  reduce (ys => x => [...ys, f (x)]) ([])
  
const add = x => y => x + y

const sq = x => x * x

console.log (range (-2) (2))
// [ -2, -1, 0, 1, 2 ]

console.log (reduce (add) (0) ([1,2,3,4]))
// 10

console.log (map (sq) ([1,2,3,4]))
// [ 1, 4, 9, 16 ]


É 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

/* const U = f => f (f)
 *
 * const Y = U (h => f => f (x => U (h) (f) (x)))
 *
 * const fibonacci = Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
 *
 */

/*
 * given fibonacci (7)
 *
 * replace fibonacci with its definition
 * Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 *
 * replace Y with its definition
 * U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
//
 * replace U with its definition
 * (f => f (f)) U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 */

let result =
  (f => f (f)) (h => f => f (x => h (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
  
console.log (result) // 13

E aí está - fibonacci (7)calculado recursivamente usando nada além de funções anônimas

Obrigado
fonte
14

Pode ser mais simples usar um "objeto anônimo":

({
  do: function() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

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.

({
  do() {
    console.log("don't run this ...");
    this.do();
  }
}).do();
svidgen
fonte
13

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.calleecomo 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:

(function foo(data){
    data++;
    var nothing = function() {
        foo(data);
    }
    nothing();
})();

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, foopolui incorretamente o escopo pai no IE, e o pai fooé uma instância separada da foovista por dentro foo.

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 nothingcada vez em torno da recursão? Você pode estar melhor com um espaço para nome contendo duas funções simples recursivas.

bobince
fonte
Concordo que uma função nomeada é mais adequada do que argumentos.calcule não apenas para o modo estrito de ecmascript, mas também por uma questão de otimização, pois a cada recursão ele precisa obter uma referência ao callee (e isso provavelmente pode reduzir a velocidade de execução )
+1 para o poético, "pushing against the boundaries of good taste"- (bem, e as boas informações).
Peter10
que tal um simples pré / pós-fixado se a poluição é realmente uma preocupação aqui? Considerando que não está no escopo global (mesmo que a função seja lvl superior, ele já deve ter uma função anônima envolvendo todo o código), é muito improvável que um nome como esse recur_foocolide com uma função no escopo pai (ou fique doente -usava) .
Gblazex 07/10/10
Muito interessante - jsfiddle.net/hck2A - o IE polui os pais nesse caso, como você disse. Nunca percebi isso.
Peter10
1
@ Peter: kangax.github.com/nfe (especialmente os 'erros do JScript') por mais do que você sempre quis saber sobre esse assunto. Finalmente foi corrigido no IE9 (mas apenas no modo de padrões do IE9).
quer
10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

fonte
34
Espero que todos que votam nessa resposta (tecnicamente correta) percebam os problemas com arguments.callee: não é permitido no modo estrito e no ES5.
Pointy
Votado, argument.callee está obsoleto no ES5 #
Jaime Rodriguez
Funciona no NodeJS. Eu não poderia me importar menos com o ES5, desde que funcione de maneira previsível em um ambiente fixo.
Angad
1
Esta é uma bomba-relógio. Não existe um ambiente chamado "fixo", como sugere o comentário acima. Você quase sempre atualizava devido a qualquer um dos milhares de motivos para fazer isso.
Sampathsris
6

Você poderia fazer algo como:

(foo = function() { foo(); })()

ou no seu caso:

(recur = function(data){
    data = data+1;
    var nothing = function() {
        if (data > 100) return; // put recursion limit
        recur(data);
    }
    nothing();
})(/* put data init value here */ 0);
ArtBIT
fonte
Você poderia fazer com declarar recurprimeiro com uma vardeclaração. Não sei se isso infringe as regras da pergunta, mas como você a possui agora, sem a vardeclaração, você receberá um erro no modo estrito do ECMAScript 5.
Tim Down
Meu comentário inicial incluiu a varpalavra - 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;)
ArtBIT
Sim, fazer (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 sugerindo var recur; (recur = function() {...})();.
Tim Down
3

Quando você declara uma função anônima como esta:

(function () {
    // Pass
}());

É 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:

(function foo () {
    foo();
}());
foo //-> undefined
xj9
fonte
"permanece anônimo" - não, não. Uma função anônima não tem um nome. Entendo que fooisso 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.
Obrigado
3

Por que não passar a função para a própria função?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);
Riccardo Bassilichi
fonte
3

Em certas situações, você precisa confiar em funções anônimas. Dada é uma mapfunção recursiva :

const map = f => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : map (f) ([...acc, f(head)]) (tail);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) ([0]) (xs)); // [0] modifies the structure of the array

Observe que mapnão deve modificar a estrutura da matriz. Portanto, o acumulador accnão precisa ser exposto. Podemos envolver mapoutra função, por exemplo:

const map = f => xs => {
  let next = acc => ([head, ...tail]) => head === undefined
   ? acc
   : map ([...acc, f(head)]) (tail);

  return next([])(xs);
}

Mas esta solução é bastante detalhada. Vamos usar o Ucombinador subestimado :

const U = f => f(f);

const map = f => U(h => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : h(h)([...acc, f(head)])(tail))([]);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) (xs));

Conciso, não é? Ué simples, mas tem a desvantagem de que a chamada recursiva fica um pouco ofuscada: sum(...)torna - se h(h)(...)- isso é tudo.


fonte
2

Não tenho certeza se a resposta ainda é necessária, mas isso também pode ser feito usando delegados criados usando function.bind:

    var x = ((function () {
        return this.bind(this, arguments[0])();
    }).bind(function (n) {
        if (n != 1) {
            return n * this.bind(this, (n - 1))();
        }
        else {
            return 1;
        }
    }))(5);

    console.log(x);

Isso não envolve funções nomeadas ou argumentos.

Nitij
fonte
1

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!

var initialValue = ...

(function recurse(data){
    data++;
    var nothing = function() {
        recurse(data);
    }
    if ( ... stop condition ... )
        { ... display result, etc. ... }
    else
        nothing();
}(initialValue));

Exemplo de trabalho jsFiddle (usa dados + = dados por diversão)


Peter Ajtai
fonte
1
+1, esta é uma resposta muito útil e você deve receber mais votos por ela, mas não é anônima.
Incognito
você claramente não ler o que bobince escreveu: However named inline function expressions are also best avoided.. Mas o OP perde o ponto também ... :)
gblazex
@ Galamb - eu li. Não permitido no modo estrito e no ES5 não é o mesmo que poluir um escopo pai e criar instâncias extras.
Peter Ajtai
1

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:

var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);

que produz uma string como 'Root: foo: bar: baz: ...'

radio_babylon
fonte
1

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:

const applyT = thunk => thunk();

const fib = n => applyT(
  (f = (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)) => f(0, 1, n)
);

console.log(fib(10)); // 55

// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

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. Quando fé invocado por applyTesta 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
0

Outra resposta que não envolve função nomeada ou argumentos.

var sum = (function(foo,n){
  return n + foo(foo,n-1);
})(function(foo,n){
     if(n>1){
         return n + foo(foo,n-1)
     }else{
         return n;
     }
},5); //function takes two argument one is function and another is 5

console.log(sum) //output : 15
jforjs
fonte
nice: vincule uma função anônima a um parâmetro local e, em seguida, chame a função pelo parâmetro local, mas também passe a função para si própria para a recursão.
Englebart
0

Este é um retrabalho da resposta do jforjs com nomes diferentes e uma entrada ligeiramente modificada.

// function takes two argument: first is recursive function and second is input
var sum = (function(capturedRecurser,n){
  return capturedRecurser(capturedRecurser, n);
})(function(thisFunction,n){
     if(n>1){
         return n + thisFunction(thisFunction,n-1)
     }else{
         return n;
     }
},5); 

console.log(sum) //output : 15

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.

englebart
fonte
0

Esta é uma versão da resposta do @ zem com funções de seta.

Você pode usar o Uou o Ycombinador. 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'))

Ricardo Freitas
fonte
0

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:

var Y = f => (x => x(x))(y => f(x => y(y)(x)));
myfirstAnswer
fonte
-1

Isso pode não funcionar em qualquer lugar, mas você pode usar arguments.calleepara se referir à função atual.

Assim, o fatorial poderia ser feito assim:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Dan Jones
fonte