JavaScript:递归匿名函数?

141

假设我有一个基本的递归函数:

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

如果我有一个匿名函数,我该怎么做呢?...

(function(data){
    data = data+1;
    var nothing = function() {
        //Something here that calls the function?
    }
    nothing();
})();

我希望有一种方法可以调用调用当前函数的函数... 我曾经在某个地方看到过脚本(我现在记不起来了)可以告诉您被调用的函数的名称,但我现在无法回想起任何与此相关的信息。


1
@thenduks:和使用匿名函数的原因一样。只是有时候需要使用递归。 - poke
5
很遗憾arguments.callee存在,而这个函数并没有做任何有用的事情。我正在查找Y组合子,该东西永远不会变得有用... - Kobi
1
是的,正如Kobi链接的那样,使用固定点组合器(例如Y)来进行匿名递归函数而无需arguments.callee。 - steamer25
1
请参考http://w3future.com/weblog/stories/2002/02/22/javascriptYCombinator.html,了解JS中Y组合子的示例。 - steamer25
这是由Y-Combinator在函数式语言中完成的。有关详细说明,请查看此链接:[https://dev59.com/qFoU5IYBdhLWcg3wzZA1#41412648]。 - Redu
21个回答

2

我不确定是否还需要答案,但是也可以使用使用 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);

这不涉及命名函数或arguments.callee。


2

在ES2015中,我们可以尝试一下语法,并滥用默认参数和thunks。后者只是没有任何参数的函数:

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

请注意,f是一个带有匿名函数(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)作为其默认值的参数。当通过applyT调用f时,必须不带参数进行调用,以便使用默认值。默认值是一个函数,因此f是一个命名函数,可以递归调用自身。

1
我需要(或者说,想要)一个一行的匿名函数来遍历一个对象并构建一个字符串,我是这样处理的:
var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);

这会生成一个字符串,类似于“根目录:foo:bar:baz:...”


1

正如bobince所写,只需为您的函数命名。

但是,我猜您还想传入一个初始值并最终停止函数!

var initialValue = ...

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

工作中的jsFiddle示例(使用data += data进行娱乐)



1
+1,这是一个非常有用的答案,你应该得到更多的赞,但它不是匿名的。 - Incognito
你显然没有读懂Bobince写的话:“但最好避免使用命名的内联函数表达式。”但是,OP也错过了重点... :) - gblazex
@Galamb - 我已经阅读了。在严格模式和ES5中被禁止并不等同于污染父级作用域并创建额外的实例。 - Peter Ajtai

0

另一个答案不涉及命名函数或arguments.callee

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

使用“nice”将一个匿名函数绑定到本地参数,然后通过本地参数调用该函数,同时将函数传递给自身进行递归。 - englebart

0

这是对jforjs答案的重新制作,使用不同的名称和稍微修改的条目。

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

没有必要展开第一次递归。函数作为引用接收自身,回溯到面向对象编程的原始起源。


0

我不建议在任何实际用例中这样做,但只是作为一种有趣的练习,您可以使用第二个匿名函数来实现!

(f => f(f))(f => {
    data = data+1;
    var nothing = function() {
        f();
    }
    nothing(f);
});

这个方法的工作原理是将匿名函数作为参数传递给自身,以便我们可以从自身调用它。

0

这是@zem答案的箭头函数版本。

你可以使用UY组合子。其中,Y组合子最简单易用。

U组合子需要不断传递函数: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Y组合子不需要不断传递函数: const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))


0

这是一个已弃用的函数,将来会被移除,因此会导致错误。参考:链接。对于旧浏览器可以使用,但对于当前和未来的浏览器,不可行 - poring91

0
另一种Y组合子的解决方案,使用rosetta-code链接(我想之前有人在stackOverflow上提到过该链接)。
箭头符号对于匿名函数来说更易读:
var Y = f => (x => x(x))(y => f(x => y(y)(x)));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接