繁体   English   中英

如何在 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 用 Ja​​vaScript 解释 所有的功劳都应该归原作者所有。 我主要只是“协调”(这就是你所说的用 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))
                      );
    }
}

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

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

丑是不是?

特尔;博士:

const rec = f => f((...xs) => rec(f)(...xs));

这里有很多答案,其中有一个适当的 Y 的变化——但这有点多余......问题是 Y 的通常解释方式是“如果没有递归会怎样”,所以 Y 本身不能指代自己。 但是因为这里的目标是一个实用的组合器,所以没有理由这样做。 这个答案使用自身定义了rec ,但它很复杂而且有点丑,因为它添加了一个参数而不是柯里化。

简单的递归定义的 Y 是

const rec = f => f(rec(f));

但由于 JS 并不懒惰,因此上面添加了必要的包装。

用于任意数量参数的递归函数定义的通用组合器(不使用自身内部的变量)将是:

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 = () => {}的事情也很糟糕,这个答案中描述的黑客可能是你唯一的选择:

javascript:递归匿名函数?

var rec = () => {rec()};
rec();

那会是一个选择吗?

我发现提供的解决方案真的很复杂,老实说,其中任何一个都看不懂,所以我自己想出了一个更简单的解决方案(我确定它已经知道了,但我的思考过程是这样的):

所以你在做一个阶乘函数

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

就是这样。 我们刚刚创建了一个将自身作为第一个参数的函数,生成了 Factorial 函数! 直接把它传给自己:

(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只是您在每次递归时所做的操作,例如乘以 Factorial 或添加 Fibonacci: x1 => x2 => x1 + x2 最后nextStep是传递给函数的下一个值,通常是当前值减一: x => x - 1 申请:

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

这是这个答案的一个版本, https://stackoverflow.com/a/3903334/689223 ,带有箭头功能。

您可以使用UY组合器。 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);

这个函数正如名称所暗示的那样,它将生成我们需要的引用。 现在我们只需要将它应用到我们的函数中

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM