arguments
属性,因此arguments.callee
将无法工作,即使只使用匿名函数,也不会在严格模式下工作。箭头函数不能被命名,因此不能使用命名函数表达式的技巧。
那么...如何编写递归箭头函数呢?也就是说,箭头函数根据某些条件进行递归调用等等。
arguments
属性,因此arguments.callee
将无法工作,即使只使用匿名函数,也不会在严格模式下工作。写一个不命名的递归函数是计算机科学本身就存在的问题(实际上更古老,因为λ-演算先于计算机科学),因为在λ-演算中所有函数都是匿名的,但你仍然需要递归。
解决方案是使用固定点组合器,通常是Y组合器。它看起来像这样:
(y =>
y(
givenFact =>
n =>
n < 2 ? 1 : n * givenFact(n-1)
)(5)
)(le =>
(f =>
f(f)
)(f =>
le(x => (f(f))(x))
)
);
这将递归计算5
的阶乘。
注意:该代码基于 用JavaScript解释Y Combinator。所有功劳应归原作者所有。我只是���其上进行了“和谐化”(用ES / Harmony的新功能重构旧代码而已)。
看起来你可以将箭头函数分配给一个变量,并使用它来递归调用函数。
var complex = (a, b) => {
if (a > b) {
return a;
} else {
complex(a, b);
}
};
complex(2,3)
会返回 InternalError: too much recursion
。 - Ortomala LokniClaus Reinke在esdiscuss.org网站的讨论中回答了你的问题。
在ES6中,你必须定义他所称的递归组合器。
let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )
如果您想调用递归箭头函数,您需要使用箭头函数作为参数调用递归组合器,箭头函数的第一个参数是一个递归函数,其余参数是参数。递归函数的名称并不重要,因为它不会在递归组合器之外使用。然后可以调用匿名箭头函数。这里我们计算6的阶乘。
rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)
如果您想在Firefox中测试它,您需要使用递归组合器的ES5翻译版本:function rec(f){
return function(){
return f.apply(this,[
function(){
return rec(f).apply(this,arguments);
}
].concat(Array.prototype.slice.call(arguments))
);
}
}
let rec = (f)=> (...args)=> f( (...args)=>rec(f)(...args), ...args ); rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6);
- aviitx => x < 2 ? x : x * (???)
(???) 是函数应该调用自身的位置,但由于您无法命名它,显而易见的解决方案是将其作为参数传递给自身。
f => x => x < 2 ? x : x * f(x-1)
然而这样做行不通。因为当我们调用 f(x-1)
时,我们正在调用该函数本身,并且我们刚刚将其参数定义为1)f
:再次是函数本身,以及2)x
:该值。嗯,我们确实有函数本身,记得吗?所以首先传递它:
f => x => x < 2 ? x : x * f(f)(x-1)
^ the new bit
就是这样。我们刚刚创建了一个函数,将自身作为第一个参数,生成阶乘函数!只需将其传递给自身即可:
(f => x => x < 2 ? x : x * f(f)(x-1))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120
你可以编写另一个函数,将其参数传递给自身,而不是重复编写该函数:
y => y(y)
并将您的阶乘制作函数传递给它:
(y => y(y))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120
嘭。这里有一个简单的公式:
(y => y(y))(f => x => endCondition(x) ? default(x) : operation(x)(f(f)(nextStep(x))))
对于一个从0加到x
的基本函数,endCondition
是需要停止递归的条件,因此x => x == 0
。 default
是在满足endCondition
后给出的最后一个值,因此为x => x
。operation
仅是每次递归中所执行的操作,例如在阶乘中进行乘法,在斐波那契数列中进行加法:x1 => x2 => x1 + x2
。最后,nextStep
是要传递给函数的下一个值,通常是当前值减一:x => x - 1
。应用:
(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15
const U = f => f(f)
。 - user6445533一个适用于任意数量参数的递归函数定义的通用组合器(不使用变量自身)是:
const rec = (le => ((f => f(f))(f => (le((...x) => f(f)(...x))))));
例如可以用这个来定义阶乘:
const factorial = rec( fact => (n => n < 2 ? 1 : n * fact(n - 1)) );
//factorial(5): 120
const reverse = rec(
rev => (
(w, start) => typeof(start) === "string"
? (!w ? start : rev(w.substring(1), w[0] + start))
: rev(w, '')
)
);
//reverse("olleh"): "hello"
或者按顺序遍历树:
const inorder = rec(go => ((node, visit) => !!(node && [go(node.left, visit), visit(node), go(node.right, visit)])));
//inorder({left:{value:3},value:4,right:{value:5}}, function(n) {console.log(n.value)})
// calls console.log(3)
// calls console.log(4)
// calls console.log(5)
// returns true
由于 arguments.callee
已被废弃 / 在严格模式下不起作用,而像 var func = () => {}
这样的做法也不好,因此像这个答案描述的那样使用 hack 可能是您唯一的选择:
func = () => {}
那样做些事情,因此可能没有任何方法,尽管您可以像我链接到的其他SO答案中使用hack。 - Jonathan Lernervar rec = () => {rec()};
rec();
U
或 Y
组合子。其中 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'))