javascript:递归匿名函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3883780/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:29:39  来源:igfitidea点击:

javascript: recursive anonymous function?

javascriptrecursionscopeanonymous-function

提问by Incognito

Let's say I have a basic recursive function:

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

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

How could I do this if I have an anonymous function such as...

如果我有一个匿名函数,例如...

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

I'd like a way to call the function that called this function... I've seen scripts somewhere (I can't remember where) that can tell you the name of a function called, but I can't recall any of that information right now.

我想要一种方法来调用调用这个函数的函数......我在某处看到过脚本(我不记得在哪里)可以告诉你被调用函数的名称,但我不记得任何一个现在的信息。

回答by Pointy

You cangive the function a name, even when you're creating the function as a value and not a "function declaration" statement. In other words:

可以为函数命名,即使您将函数创建为值而不是“函数声明”语句。换句话说:

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

is a stack-blowing recursive function. Now, that said, you probably don'tmay not want to do thisin general because there are some weird problems with various implementations of Javascript. (note— that's a fairly old comment; some/many/all of the problems described in Kangax's blog post may be fixed in more modern browsers.)

是一个堆栈溢出的递归函数。现在,也就是说,您可能不想这样做,因为 Javascript 的各种实现存在一些奇怪的问题。(注意——这是一个相当古老的评论;Kangax 的博客文章中描述的一些/许多/所有问题可能会在更现代的浏览器中得到修复。)

When you give a name like that, the name is not visible outside the function (well, it's not supposed to be; that's one of the weirdnesses). It's like "letrec" in Lisp.

当您给出这样的名称时,该名称在函数之外是不可见的(好吧,它不应该是;这是奇怪之处之一)。这就像 Lisp 中的“letrec”。

As for arguments.callee, that's disallowed in "strict" mode and generally is considered a bad thing, because it makes some optimizations hard. It's also much slower than one might expect.

至于arguments.callee,这在“严格”模式下是不允许的,通常被认为是一件坏事,因为它使一些优化变得困难。它也比人们预期的要慢得多。

edit— If you want to have the effect of an "anonymous" function that can call itself, you can do something like this (assuming you're passing the function as a callback or something like that):

编辑——如果你想要一个可以调用自己的“匿名”函数的效果,你可以做这样的事情(假设你将函数作为回调或类似的东西传递):

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

What that does is define a function with a nice, safe, not-broken-in-IE function declarationstatement, creating a local function whose name will not pollute the global namespace. The wrapper (truly anonymous) function just returns that local function.

这样做是用一个漂亮的、安全的、不被破坏的 IE 函数声明语句定义一个函数,创建一个名称不会污染全局命名空间的局部函数。包装器(真正匿名)函数只返回该本地函数。

回答by zem

People talked about the Y combinator in comments, but no one wrote it as an answer.

人们在评论中谈论 Y 组合子,但没有人将其写为答案。

The Y combinator can be defined in javascript as follows: (thanks to steamer25 for the link)

Y 组合器可以在 javascript 中定义如下:(感谢 steamer25 提供链接)

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

And when you want to pass your anonymous function:

当您想传递匿名函数时:

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

The most important thing to note about this solution is that you shouldn't use it.

关于此解决方案需要注意的最重要的一点是您不应该使用它。

回答by Thank you

U combinator

U组合子

By passing a function to itself as an argument, a function can recur using its parameter instead of its name! So the function given to Ushould have at least one parameter that will bind to the function (itself).

通过将函数作为参数传递给自身,函数可以使用其参数而不是名称来递归!所以给定的函数U应该至少有一个参数可以绑定到函数(本身)。

In the example below, we have no exit condition, so we will just loop indefinitely until a stack overflow happens

在下面的例子中,我们没有退出条件,所以我们将无限循环直到发生堆栈溢出

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

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

We can stop the infinite recursion using a variety of techniques. Here, I'll write our anonymous function to return anotheranonymous function that's waiting for an input; in this case, some number. When a number is supplied, if it is greater than 0, we will continue recurring, otherwise return 0.

我们可以使用多种技术来停止无限递归。在这里,我将编写匿名函数以返回另一个等待输入的匿名函数;在这种情况下,一些数字。当提供一个数字时,如果它大于0,我们将继续重复,否则返回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

What's not immediately apparent here is that our function, when first applied to itself using the Ucombinator, it returns a function waiting for the first input. If we gave a name to this, can effectively construct recursive functions using lambdas (anonymous functions)

这里不是很明显的是,当我们的函数第一次使用U组合子应用于自身时,它返回一个等待第一个输入的函数。如果我们给它一个名字,可以使用 lambdas(匿名函数)有效地构造递归函数

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

Only this isn't directrecursion – a function that calls itself using its own name. Our definition of countDowndoes not reference itself inside of its body and still recursion is possible

只是这不是直接递归——一个使用自己的名字调用自己的函数。我们的定义countDown没有在其主体内部引用自身,并且仍然可以递归

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


How to remove self-reference from an existing function using U combinator

如何使用 U 组合器从现有函数中删除自引用

Here I'll show you how to take a recursive function that uses a reference to itself and change it to a function that employs the U combinator to in place of the self reference

在这里,我将向您展示如何将使用对自身的引用的递归函数更改为使用 U 组合子来代替自引用的函数

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

Now using the U combinator to replace the inner reference to factorial

现在使用 U 组合器替换内部引用 factorial

const U = f => f (f)

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

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

The basic replacement pattern is this. Make a mental note, we will be using a similar strategy in the next section

基本的替换模式是这样的。记下,我们将在下一节中使用类似的策略

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

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


Y combinator

Y组合子

related: the U and Y combinators explained using a mirror analogy

相关:U 和 Y 组合子使用镜像类比解释

In the previous section we saw how to transform self-reference recursion into a recursive function that does not rely upon a named function using the U combinator. There's a bit of an annoyance tho with having to remember to always pass the function to itself as the first argument. Well, the Y-combinator builds upon the U-combinator and removes that tedious bit. This is a good thing because removing/reducing complexity is the primary reason we make functions

在上一节中,我们看到了如何使用 U 组合子将自引用递归转换为不依赖命名函数的递归函数。必须记住始终将函数作为第一个参数传递给自身,这有点令人烦恼。嗯,Y-combinator 建立在 U-combinator 的基础上,去掉了那个乏味的部分。这是一件好事,因为消除/降低复杂性是我们制作函数的主要原因

First, let's derive our very own Y-combinator

首先,让我们推导出我们自己的 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)))

Now we will see how it's usage compares to the U-combinator. Notice, to recur, instead of U (f)we can simply call f ()

现在我们将看看它的用法与 U-combinator 相比如何。注意,要重复,U (f)我们可以简单地调用f ()

const U = f => f (f)

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

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

Now I'll demonstrate the countDownprogram using Y– you'll see the programs are almost identical but the Y combinator keeps things a bit cleaner

现在我将countDown使用该程序进行演示Y- 您会看到这些程序几乎相同,但 Y 组合器使事情更简洁

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

And now we'll see factorialas well

现在,我们可以看到factorial,以及

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

As you can see, fbecomes the mechanism for recursion itself. To recur, we call it like an ordinary function. We can call it multiple times with different arguments and the result will still be correct. And since it's an ordinary function parameter, we can name it whatever we like, such as recurbelow -

如您所见,f成为递归本身的机制。为了重现,我们将其称为普通函数。我们可以用不同的参数多次调用它,结果仍然是正确的。并且因为它是一个普通的函数参数,我们可以随意命名它,比如recur下面——

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



U and Y combinator with more than 1 parameter

具有 1 个以上参数的 U 和 Y 组合器

In the examples above, we saw how we can loop and pass an argument to keep track of the "state" of our computation. But what if we need to keep track of additional state?

在上面的示例中,我们看到了如何循环并传递参数以跟踪计算的“状态”。但是如果我们需要跟踪额外的状态呢?

We coulduse compound data like an Array or something...

我们可以使用像数组之类的复合数据……

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

But this is bad because it's exposing internal state (counters aand b). It would be nice if we could just call fibonacci (7)to get the answer we want.

但这很糟糕,因为它暴露了内部状态(计数器ab)。如果我们能打电话fibonacci (7)得到我们想要的答案,那就太好了。

Using what we know about curried functions (sequences of unary (1-paramter) functions), we can achieve our goal easily without having to modify our definition of Yor rely upon compound data or advanced language features.

使用我们对柯里化函数(一元(1 参数)函数的序列)的了解,我们可以轻松实现我们的目标,而无需修改Y或依赖复合数据或高级语言功能的定义。

Look at the definition of fibonacciclosely below. We're immediately applying 0and 1which are bound to aand brespectively. Now fibonacci is simply waiting for the last argument to be supplied which will be bound to x. When we recurse, we must call f (a) (b) (x)(not f (a,b,x)) because our function is in curried form.

fibonacci仔细看看下面的定义。我们立即申请0and 1which 分别绑定到ab。现在斐波那契只是等待提供最后一个参数,该参数将绑定到x。当我们递归时,我们必须调用f (a) (b) (x)(not f (a,b,x)),因为我们的函数是柯里化的。

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



This sort of pattern can be useful for defining all sorts of functions. Below we'll see two more functions defined using the Ycombinator (rangeand reduce) and a derivative of reduce, map.

这种模式可用于定义各种函数。下面我们将看到使用定义的两个函数Y组合子(rangereduce)和衍生物reducemap

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 ]



IT'S ALL ANONYMOUS OMG

这都是匿名的 OMG

Because we're working with pure functions here, we can substitute any named function for its definition. Watch what happens when we take fibonacci and replace named functions with their expressions

因为我们在这里使用纯函数,所以我们可以用任何命名函数替换它的定义。观察当我们采用斐波那契并将命名函数替换为它们的表达式时会发生什么

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

And there you have it – fibonacci (7)calculated recursively using nothing but anonymous functions

你有它 - 只fibonacci (7)使用匿名函数递归计算

回答by svidgen

It may be simplest to use an "anonymous object" instead:

使用“匿名对象”可能是最简单的:

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

Your global space is completely unpolluted. It's pretty straightforward. And you can easily take advantage of the object's non-global state.

您的全球空间是完全未受污染的。这很简单。并且您可以轻松利用对象的非全局状态。

回答by bobince

I would not do this as an inline function. It's pushing against the boundaries of good taste and doesn't really get you anything.

我不会将其作为内联函数来执行。它突破了品味的界限,并没有真正让你得到任何东西。

If you really must, there is arguments.calleeas in Fabrizio's answer. However this is generally considered inadvisable and is disallowed in ECMAScript Fifth Edition's ‘strict mode'. Although ECMA 3 and non-strict-mode are not going away, working in strict mode promises more possible language optimisations.

如果您真的必须这样做,那么arguments.calleeFabrizio 的答案就是如此。然而,这通常被认为是不可取的,并且在 ECMAScript Fifth Edition 的“严格模式”中是不允许的。尽管 ECMA 3 和非严格模式不会消失,但在严格模式下工作有望实现更多可能的语言优化。

One can also use a named inline function:

还可以使用命名的内联函数:

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

However named inline function expressions are also best avoided, as IE's JScript does some bad things to them. In the above example fooincorrectly pollutes the parent scope in IE, and the parent foois a separate instance to the fooseen inside foo.

然而,最好避免命名内联函数表达式,因为 IE 的 JScript 对它们做了一些不好的事情。在上面的例子中foo错误地污染了 IE 中的父作用域,而父作用域foo是一个单独的实例,可以foo看到里面的foo.

What's the purpose of putting this in an inline anonymous function? If you just want to avoid polluting the parent scope, you can of course hide your first example inside another self-calling-anonymous-function (namespace). Do you really need to create a new copy of nothingeach time around the recursion? You might be better off with a namespace containing two simple mutually-recursive functions.

将其放入内联匿名函数的目的是什么?如果您只想避免污染父作用域,您当然可以将第一个示例隐藏在另一个自调用匿名函数(命名空间)中。你真的需要nothing每次围绕递归创建一个新副本吗?使用包含两个简单的相互递归函数的命名空间可能会更好。

回答by bobince

(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

回答by ArtBIT

You could do something like:

你可以这样做:

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

or in your case:

或者在你的情况下:

(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);

回答by ArtBIT

In certain situations you have to rely on anonymous functions. Given is a recursive mapfunction:

在某些情况下,您必须依赖匿名函数。给定的是一个递归map函数:

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

Please note that mapmust not modify the structure of the array. So the accumulator accneedn't to be exposed. We can wrap mapinto another function for instance:

请注意,map一定不要修改数组的结构。所以蓄能器acc不需要暴露。例如,我们可以包装map到另一个函数中:

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

  return next([])(xs);
}

But this solution is quite verbose. Let's use the underestimated Ucombinator:

但是这个解决方案非常冗长。让我们使用被低估的U组合器:

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));

Concise, isn't it? Uis dead simple but has the disadvantage that the recursive call gets a bit obfuscated: sum(...)becomes h(h)(...)- that's all.

简明扼要,不是吗?U非常简单,但缺点是递归调用有点混淆:sum(...)变成h(h)(...)- 仅此而已。

回答by Riccardo Bassilichi

Why not pass the function to the functio itself ?

为什么不将函数传递给函数本身?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);

回答by xj9

When you declare an anonymous function like this:

当你像这样声明一个匿名函数时:

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

Its considered a function expression and it has an optional name (that you can use to call it from within itself. But because it's a function expression (and not a statement) it stays anonymous (but has a name that you can call). So this function can call itself:

它被认为是一个函数表达式,它有一个可选名称(您可以使用它从自身内部调用它。但因为它是一个函数表达式(而不是语句),所以它保持匿名(但有一个您可以调用的名称)。所以这个函数可以调用自己:

(function foo () {
    foo();
}());
foo //-> undefined