在JavaScript中为`reduceRight()`函数实现惰性评估

时间:2019-07-05 15:33:52

标签: javascript ecmascript-6 lazy-evaluation

codewars.com中的一个问题: https://www.codewars.com/kata/foldr/train/javascript

为数组定义一个折叠函数,该函数实现与内置函数reduceRight()相同的功能。这个问题非常抽象,因为我对函数式编程知之甚少。

我发现最重要的是如何在JavaScript中实现延迟评估。但是我不知道如何处理。

例如,我们有两个函数indexOfloggingindexOf(x)是一个函数,将在foldr方法{{1} }是一个包装函数,它告诉我们logging可以被调用多少次。

indexOf(x)

如果我们不懒惰地实现它并使用递归:

const indexOf = y => function (cur, acc) {
  if (cur === y) {
    return 0
  } else {
    return acc + 1 || -1
  }
};

const logging = fn => function logging(...a) {
  i++;
  return fn(...a);
};

变量Object.defineProperty(Array.prototype, "foldr", { value: function foldr(fn, z) { return function _foldr(a) { if (a.length === 0) { return z } return fn(a[0], _foldr(a.slice(1))) }(this); } }); let i = 0 let x = [1, 2, 3].foldr(logging(indexOf(1)), -1) console.log(`x: ${x}`) // x: 0 console.log(`i: ${i}`) // i: 3 显示该函数已被调用3次,整个数组已被迭代。但是,如果我们观察到i函数,就会发现如果我们使用惰性求值,就不需要迭代整个数组。

在第一级递归中 indexOf等于indexOf(1)(a[0], _foldr(a.slice(1))),因为indexOf(1)(1, _foldr([2,3]))应当立即返回0,并且不需要计算第二个参数cur === y。因此,在codewars.com的测试用例中,_foldr([2,3])应该为1。

我该如何处理?

2 个答案:

答案 0 :(得分:0)

您近在咫尺...:

  fn(a[0], () => _foldr(a.slice(1)))

如果您传递函数而不是acc累加器值,则该函数可以决定是否评估累加器:

  const indexOf = y => function (cur, acc) {
    if (cur === y) {
      return 0; // acc() was not called, ends here
    } else {
      return acc() + 1 || -1; // acc() gets called, traversal goes on
    }
 };

答案 1 :(得分:0)

我找到了解决方案。使用Object.prototype.valueOf()

  

valueOf()方法返回指定对象的原始值。

因为我们想在执行诸如return acc + 1 || -1之类的算法时调用该函数。

我们可以使用:

Object.defineProperty(Array.prototype, 'foldr', {
  value(fn, z) {
      let _foldr = (a) => {
          if (!a.length) return z;
          let r = fn(a[0], { valueOf: () => _foldr(a.slice(1)) });
          return (r.valueOf) ? r.valueOf() : r;
      };
      return _foldr(this);
  },
});