如何在 ES6 中递归编写箭头函数
How do I write an arrow function in ES6 recursively?
ES6 中的箭头函数没有 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
的阶乘。
注意:代码在很大程度上基于此:Y Combinator 用 JavaScript 解释。所有功劳都应归原作者所有。我主要只是"协调"(这就是你所说的用 ES/Harmony 的新功能重构旧代码吗?(它。
看起来您可以将箭头函数分配给变量并使用它来递归调用该函数。
var complex = (a, b) => {
if (a > b) {
return a;
} else {
complex(a, b);
}
};
Claus 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))
);
}
}
TL;DR:
const rec = f => f((...xs) => rec(f)(...xs));
这里有很多答案,有适当的Y的变化 - 但这有点多余...... 问题是,通常解释Y的方式是"如果没有递归怎么办",所以Y本身不能引用自己。 但是,由于这里的目标是一个实用的组合器,因此没有理由这样做。 有这个答案定义了rec
使用自身,但它很复杂,有点丑陋,因为它增加了一个参数而不是咖喱。
简单的递归定义 Y 是
const rec = f => f(rec(f));
但是由于JS并不懒惰,因此上面添加了必要的包装。
使用您为其分配函数的变量,例如
const fac = (n) => n>0 ? n*fac(n-1) : 1;
如果你真的需要匿名,请使用 Y 运算器,如下所示:
const Y = (f) => ((x)=>f((v)=>x(x)(v)))((x)=>f((v)=>x(x)(v)))
… Y((fac)=>(n)=> n>0 ? n*fac(n-1) : 1) …
(丑陋,不是吗?
我发现提供的解决方案真的很复杂,老实说无法理解其中任何一个,所以我自己想出了一个更简单的解决方案(我确定它已经知道了,但这是我的思考过程(:
所以你正在做一个阶乘函数
x => x < 2 ? x : x * (???)
(???(是函数应该调用自己的位置,但由于您无法命名它,因此明显的解决方案是将其作为参数传递给自身
f => x => x < 2 ? x : x * f(x-1)
因为当我们调用f(x-1)
时,我们调用的是这个函数本身,我们只是将其参数定义为 1( f
:函数本身,再次和 2( x
值。好吧,我们确实有函数本身,f
记得吗?所以先通过它:
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
是传递给函数的下一个值,通常是当前值减去 1:x => x - 1
。应用:
(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15
用于任意数量参数的递归函数定义的通用组合器(不使用自身内部的变量(将是:
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
var rec = () => {rec()};
rec();
这是一种选择吗?
由于arguments.callee
由于弃用/在严格模式下不起作用而是一个糟糕的选择,并且做类似var func = () => {}
的事情也很糟糕,因此像这个答案中描述的黑客可能是你唯一的选择:
JavaScript:递归匿名函数?
这是这个答案的一个版本,带有箭头函数。
您可以使用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'))
您可以将函数分配给 iife 中的变量
var countdown = f=>(f=a=>{
console.log(a)
if(a>0) f(--a)
})()
countdown(3)
//3
//2
//1
//0
我认为最简单的解决方案是查看您唯一没有的东西,即对函数本身的引用。 因为如果你有这个,那么回避是微不足道的。
令人惊讶的是,这可以通过高阶函数实现。
let generateTheNeededValue = (f, ...args) => f(f, ...args);
这个函数作为名称 Sugests,它将生成我们需要的引用。 现在我们只需要将其应用于我们的函数
(generateTheNeededValue)(ourFunction, ourFunctionArgs)
但是使用这个东西的问题在于,我们的函数定义需要期待一个非常特殊的第一个参数。
let ourFunction = (me, ...ourArgs) => {...}
我喜欢把这个特殊的价值称为"我"。 现在每次我们需要递归时,我们都是这样做的。
me(me, ...argsOnRecursion);
有了所有这些。我们现在可以创建一个简单的阶乘函数。
((f, ...args) => f(f, ...args))((me, x) => {
if(x < 2) {
return 1;
} else {
return x * me(me, x - 1);
}
}, 4)
-> 24
我也喜欢看这个的一行
((f, ...args) => f(f, ...args))((me, x) => (x < 2) ? 1 : (x * me(me, x - 1)), 4)
这是递归函数 js es6 的示例。
let filterGroups = [
{name: 'Filter Group 1'}
];
const generateGroupName = (nextNumber) => {
let gN = `Filter Group ${nextNumber}`;
let exists = filterGroups.find((g) => g.name === gN);
return exists === undefined ? gN : generateGroupName(++nextNumber); // Important
};
let fg = generateGroupName(filterGroups.length);
filterGroups.push({name: fg});
- 递归函数中断
- 在递归生成器函数中,yield后面的*(星号/星号)语法意味着什么
- 递归|两个函数名
- 将jQuery对象传递到setTimeout递归函数中
- 为什么递归生成器函数没有't在ES2015工作
- 具有嵌套对象数组的 Javascript 对象的递归搜索函数
- 对象与递归函数的比较
- 循环内部的递归函数未按预期工作
- 递归函数返回不正确
- 递归函数编程困境
- 给定一个带有数字的数组,我如何编写一个递归函数,当 2 个元素加起来为一个目标时,它会在数组中查找索引
- JavaScript中的异步函数递归和Bluebird Promises
- 是什么导致了“;无方法”;函数递归调用自身时出错
- bind函数递归传递参数失败
- 使用javascript函数递归地创建一个星形三角形
- 尝试执行两个函数递归时,未定义一个函数
- 如何停止函数递归
- 在javascript中,使用回调函数递归地进行循环控制是很危险的
- 为什么Safari调用函数.递归地应用
- 在javascript中实现函数递归findById有比这更好的方法吗?