Javascript 如何在ES6中递归地编写箭头函数?

Javascript 如何在ES6中递归地编写箭头函数?,javascript,recursion,ecmascript-6,arrow-functions,Javascript,Recursion,Ecmascript 6,Arrow Functions,ES6中的箭头函数没有arguments属性,因此arguments.callee将无法工作,并且即使只使用匿名函数,也无法在严格模式下工作 无法命名箭头函数,因此无法使用命名函数表达式技巧 所以。。。如何编写递归箭头函数?这是一个arrow函数,它根据某些条件递归地调用自身,当然如此?因为参数。被调用方是一个不好的选项,因为不赞成/在严格模式下不工作,并且执行类似var func=()=>{}的操作也是不好的,这个答案中描述的黑客可能是您唯一的选择: 看起来您可以将箭头函数分配给变量,并使用它

ES6中的箭头函数没有
arguments
属性,因此
arguments.callee
将无法工作,并且即使只使用匿名函数,也无法在严格模式下工作

无法命名箭头函数,因此无法使用命名函数表达式技巧


所以。。。如何编写递归箭头函数?这是一个arrow函数,它根据某些条件递归地调用自身,当然如此?

因为
参数。被调用方
是一个不好的选项,因为不赞成/在严格模式下不工作,并且执行类似
var func=()=>{}
的操作也是不好的,这个答案中描述的黑客可能是您唯一的选择:


看起来您可以将箭头函数分配给变量,并使用它递归调用函数

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        complex(a, b);
    }
};

这是一种选择吗

克劳斯·莱因克在一次关于这个问题的讨论中回答了你的问题

在ES6中,您必须定义他所称的递归组合器

 let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )
如果要调用递归arrow函数,必须以arrow函数作为参数调用递归组合器,arrow函数的第一个参数是递归函数,其余参数是参数。递归函数的名称并不重要,因为它不会在递归组合器之外使用。然后可以调用匿名箭头函数。这里我们计算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))
                      );
    }
}

使用分配函数的变量,例如

const fac = (n) => n>0 ? n*fac(n-1) : 1;
如果您确实需要匿名,请使用,如下所示:

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) …
(y => 
  y(
    givenFact => 
      n => 
        n < 2 ? 1 : n * givenFact(n-1)
  )(5)
)(le => 
  (f => 
    f(f)
  )(f => 
    le(x => (f(f))(x))
  )
);

()

编写一个没有命名的递归函数是一个与计算机科学本身一样古老的问题(事实上,甚至更古老,因为λ演算早于计算机科学),因为在λ演算中,所有函数都是匿名的,但你仍然需要递归

解决方法是使用不动点组合子,通常是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) …
(y => 
  y(
    givenFact => 
      n => 
        n < 2 ? 1 : n * givenFact(n-1)
  )(5)
)(le => 
  (f => 
    f(f)
  )(f => 
    le(x => (f(f))(x))
  )
);
(y=>
y(
givenFact=>
n=>
n<2?1:n*givenFact(n-1)
)(5)
)(乐=>
(f=>
f(f)
)(f=>
le(x=>(f(f))(x))
)
);
这将递归计算
5
的阶乘

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        complex(a, b);
    }
};

注意:代码主要基于以下内容:。所有的功劳都应该归于原作者。我基本上只是“协调”(这就是你所说的用ES/Harmony的新特性重构旧代码吗?)它。

你可以将你的函数分配给iife中的一个变量

var countdown = f=>(f=a=>{
  console.log(a)
  if(a>0) f(--a)
})()

countdown(3)

//3
//2
//1
//0

对于任意数量参数的递归函数定义(不在其内部使用变量),通用组合器应为:

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

这是这个答案的一个版本,带有箭头功能

您可以使用
U
Y
组合器。Y组合器是使用最简单的

U
combinator,使用此函数,您必须不断传递函数:

常数U=f=>f(f)
U(selfFn=>arg=>selfFn(selfFn)(“到无穷远”)

Y
combinator,使用它您不必一直传递函数:

常数Y=gen=>U(f=>gen(…args)=>f(f)(…args)))
Y(selfFn=>arg=>selfFn('to infinity and beyond'))

我发现提供的解决方案非常复杂,老实说,我无法理解其中的任何一个,所以我自己想出了一个更简单的解决方案(我相信它已经知道了,但我的思考过程如下):

所以你在做一个阶乘函数

x => x < 2 ? x : x * (???)
就这样。我们刚刚做了一个函数,它以自身为第一个参数,生成阶乘函数!直接把它传递给自己:

(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
对于将数字从0添加到
x
的基本函数,
endCondition
是您需要停止重复出现的时间,因此
x=>x==0
default
是您在满足
endCondition
后给出的最后一个值,因此
x=>x
<代码>操作就是您对每个递归所做的操作,比如乘阶乘或加上斐波那契:
x1=>x2=>x1+x2
。最后,
nextStep
是传递给函数的下一个值,通常是当前值减去一:
x=>x-1
。适用于:

(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15

我认为最简单的解决方案是看你唯一没有的东西,那就是对函数本身的引用。因为如果你有这样的想法,那么回避是微不足道的

令人惊讶的是,通过一个高阶函数,这是可能的

let generateTheNeededValue = (f, ...args) => f(f, ...args);
((f, ...args) => f(f, ...args))((me, x) => {
  if(x < 2) {
    return 1;
  } else {
    return x * me(me, x - 1);
  }
}, 4)

-> 24
这个函数名为sugests,它将生成我们需要的引用。现在我们只需要把它应用到我们的函数中

(generateTheNeededValue)(ourFunction, ourFunctionArgs)
但是使用这个东西的问题是我们的函数定义需要一个非常特殊的第一个参数

let ourFunction = (me, ...ourArgs) => {...}
我喜欢把这个特殊值称为“我”。现在每当我们需要递归时,我们都是这样做的

me(me, ...argsOnRecursion);
所有这些。我们现在可以创建一个简单的阶乘函数

let generateTheNeededValue = (f, ...args) => f(f, ...args);
((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)=>{
if(x<2){
返回1;
}否则{
返回x*me(me,x-1);
}
}, 4)
-> 24
我也喜欢看这一行

((f, ...args) => f(f, ...args))((me, x) => (x < 2) ? 1 : (x * me(me, x - 1)), 4)
((f,…args)=>f(f,…args))((me,x)=>(x<2)?1:(x*me(me,x-1)),4)
TL;博士:

const rec = f => f((...xs) => rec(f)(...xs));
这里有很多答案,但有点多余。。。问题是,通常解释Y的方式是“如果没有递归怎么办”,因此Y本身不能引用自己。但是因为这里的目标是一个实用的组合,所以没有理由这么做。有一种方法定义了使用自身的
rec
,但它很复杂,而且有点难看,因为它添加了一个参数而不是curry

简单的递归定义Y是

const rec = f => f(rec(f));
但是既然JS是
 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});