【问题标题】:A reimplementation of _.reduce()_.reduce() 的重新实现
【发布时间】:2021-05-28 23:25:24
【问题描述】:

下面的代码是 _.reduce() 方法的重新实现。这不是我的,但我正在使用它来掌握 _.reduce 的工作原理。它目前在两项测试中失败:

  1. 应该能够将集合减少到单个值 - AssertionError: expected 'aabcdabcdabcdabcd' to equal 'abcd'
  2. 应该支持初始状态 AssertionError:预期 'initabcdabcdabcdabcd' 等于 'initabcd'
_.reduce = function (list, iteratee, memo, context) {
  if (context) iteratee = iteratee.bind(context);
  _.each(list, (elem, index) => {
    if (memo === undefined) {
      memo = elem;
      memo =iteratee(memo, elem, index, list);
    } else memo = iteratee(memo, elem, index, list);
  });
  return memo;
};

我不明白为什么会这样。在我看来,这似乎应该按预期运行。谁能提供更多信息?

更新 由于@georg 发现了我的 _.each() 函数的问题,我能够解决第二个错误。第一个错误仍然存​​在,但略有不同:

  1. 应该能够将集合减少到单个值 AssertionError: 预期 'aabcd' 等于 'abcd'

这是与错误消息相关的测试代码

var mocks = {
  arr: ['a','b','c','d'], // >= 4 elements, all should be truthy
  obj: {a:1,b:2,c:3,d:4}, // >= 4 values, all should be truthy
  halfTruthyArr: [null,'b',null,'d'], // >= 4 elements, half should be falsy
  halfTruthyObj: {a:1,b:null,c:3,d:null}, // >= 4 values, half should be falsy
  string: 'This is a string.',
  reverseString: function (string) {
    if (typeof string === 'string') return string.split('').reverse().join('');
  }
};

   describe('reduce', function () {

    afterEach(function () {
      called = false;
    });

    it('should be able to reduce a collection to a single value', function () {
      _.reduce(mocks.arr, function (accumulator, el, i, arr) {
        arr[i].should.equal(el);
        return accumulator.toString() + el.toString();
      }).should.equal(mocks.stringifiedArrElms);
    });

【问题讨论】:

  • 期望'abcd''initabcd'运行的代码是什么?
  • 如果你以可运行的形式提供它会更容易回答/调试。
  • @ThomasDeniffel 我是一个真正的新手,所以我不确定你所说的“可运行形式”是什么意思。我试图包含测试文件,但它们太长无法评论。你能澄清一下你的意思吗?

标签: javascript underscore.js reduce memo


【解决方案1】:

这是错误的:

  memo = elem[0];
  memo =iteratee(memo, elem, index, list);

应该是

  memo = elem

基本上,当没有给出初始值时,你应该将第一个元素作为当前值,不要对其调用回调。

_ = {}

_.each = (a, fn) => a.forEach(fn)

_.reduce = function (list, iteratee, memo, context) {
    if (context) iteratee = iteratee.bind(context);
    _.each(list, (elem, index) => {
        if (memo === undefined)
            memo = elem;
        else
            memo = iteratee(memo, elem, index, list);
    });
    return memo;
};


console.log(_.reduce([1], (a, x) => a + '|' + x))
console.log(_.reduce([1, 2, 3], (a, x) => a + '|' + x))

console.log(_.reduce([1], (a, x) => a + '|' + x, '@'))
console.log(_.reduce([1, 2, 3], (a, x) => a + '|' + x, '@'))

当然,memo === undefined 非常幼稚(没有什么能阻止回调在中间某处返回 undefined),更安全的选择是

let noMemo = Symbol();
if (arguments.length < 3)
    memo = noMemo;

_.each(list, (elem, index) => {
    if (memo === noMemo)
        memo = elem;
    else...

【讨论】:

  • 感谢您的回答。不幸的是,即使您进行了更正,相同的测试仍然失败,但错误略有不同 - 1. 应该能够将集合减少到单个值‣ AssertionError: expected 'aabcdabcdabcdabcd' to equal 'abcd' 2. 应该支持初始状态‣ AssertionError: 预期 'initabcdabcdabcdabcd' 等于 'initabcd'
  • @jahe:分享_.each的来源
  • ` _.each = function (collection, iteratee, context) { if (Array.isArray(collection)) { for (let key of collection) { for (let i = 0; i
  • @jahe:好吧,这也是错误的。您正在迭代一个数组两次 - 请注意两个嵌套的 for 语句。
  • 所以你认为最好的解决方案是在 _.reduce() 中完全不使用_.each() 吗?我想避免更改 _.each() 函数,因为它目前正在通过所有测试
【解决方案2】:

georg 已经在他的回答中指出了这一点,但我想我可以更明确一点:你使用了第一个元素两次。您的代码的以下 sn-p 旨在在未提供初始累加器时使用列表的第一个元素:

    if (memo === undefined) {
      memo = elem;
      memo =iteratee(memo, elem, index, list);
    }

我们可以在赋值中用elem 代替memo,看看第一个元素最终是如何被使用两次的:

    if (memo === undefined) {
      memo =iteratee(elem, elem, index, list);
    }

解决方案是简单地不在第一个元素上调用 iteratee,正如 georg 已经暗示的那样:

    if (memo === undefined) {
      memo = elem;
    }

georg 还建议使用唯一的Symbol() 来更可靠地指示第一个元素,而不是undefined,后者也可能是集合中iteratee 的返回值。另一种方法是简单地使用布尔状态变量(下面命名为first):

_.reduce = function (list, iteratee, memo, context) {
  var first = false;
  if (arguments.length < 3) {
    first = true;
  } else if (context) {
    iteratee = iteratee.bind(context);
  }
  _.each(list, (elem, index) => {
    if (first) {
      memo = elem;
      first = false;
    } else memo = iteratee(memo, elem, index, list);
  });
  return memo;
};

【讨论】:

    猜你喜欢
    • 2013-10-14
    • 2021-01-17
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    • 2016-04-04
    • 2018-11-12
    • 2020-09-26
    相关资源
    最近更新 更多