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
javascript: recursive anonymous 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 U
should 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 U
combinator, 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 countDown
does 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
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 countDown
program 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 factorial
as 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, f
becomes 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 recur
below -
如您所见,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 a
and b
). It would be nice if we could just call fibonacci (7)
to get the answer we want.
但这很糟糕,因为它暴露了内部状态(计数器a
和b
)。如果我们能打电话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 Y
or rely upon compound data or advanced language features.
使用我们对柯里化函数(一元(1 参数)函数的序列)的了解,我们可以轻松实现我们的目标,而无需修改Y
或依赖复合数据或高级语言功能的定义。
Look at the definition of fibonacci
closely below. We're immediately applying 0
and 1
which are bound to a
and b
respectively. 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
仔细看看下面的定义。我们立即申请0
and 1
which 分别绑定到a
和b
。现在斐波那契只是等待提供最后一个参数,该参数将绑定到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 Y
combinator (range
and reduce
) and a derivative of reduce
, map
.
这种模式可用于定义各种函数。下面我们将看到使用定义的两个函数Y
组合子(range
和reduce
)和衍生物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 ]
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.callee
as 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.callee
Fabrizio 的答案就是如此。然而,这通常被认为是不可取的,并且在 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 foo
incorrectly pollutes the parent scope in IE, and the parent foo
is a separate instance to the foo
seen 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 nothing
each 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 map
function:
在某些情况下,您必须依赖匿名函数。给定的是一个递归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 map
must not modify the structure of the array. So the accumulator acc
needn't to be exposed. We can wrap map
into 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 U
combinator:
但是这个解决方案非常冗长。让我们使用被低估的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? U
is 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