从头开始创建下划线缩减功能

时间:2016-06-28 23:40:02

标签: javascript underscore.js reduce

我正在创建自己的回调函数和更高阶函数组。我坚持复制下划线缩减功能或._reduce函数。有人可以帮助我理解它是如何在引擎盖下工作的,对我来说已经有几天了,我很难过。这是我到目前为止所拥有的。请理解我没有使用下划线库,我试图复制它,以便我可以进一步理解更高阶函数。感谢。

var reduce = function(collection, iterator, accumulator) {

    var iterator = function(startPoint, combiner){
      for(var i = 0; i <combiner.length; i++){
        startPoint += combiner[i];
    }
    return iterator(accumulator, collection);
}

3 个答案:

答案 0 :(得分:4)

一个简单的递归函数可以解决这个问题

// arr - some array of values
// f   - the reducing function
// acc - initial value for the accumulator
function reduce(arr, f, acc) {
  if (arr.length === 0)
    return acc
  else
    return reduce(arr.slice(1), f, f(acc, arr[0]))
}

// --------------------------------------------------
   
// example 1:
// reduce an array of numbers using an adding function

var ex1 = reduce([1,2,3], function(acc, x) { return acc + x }, 0)

console.log(ex1)
//=> 6

// --------------------------------------------------

// example 2:
// reduce an array of pairs to a mapping object

var ex2 = reduce([['a', 1], ['b', 2], ['c', 3]], function(acc, pair) {
  var key = pair[0]
  var value = pair[1]
  acc[key] = value
  return acc
}, {})

console.log(ex2)
//=> { a: 1, b: 2, c: 3 }

正如@torazaburo在评论中指出的那样,如果你可以使用ES6,那么解构赋值可以更清楚地实现实现

// ES6
function reduce([x, ...xs], f, acc) {
  if (x === undefined)
    return acc
  else
    return reduce(xs, f, f(acc, x))
}

或者它带有箭头功能的超甜蜜味道

// ES6, same as above but using arrow function and ternary expression
const reduce = ([x, ...xs], f, acc)=>
  x === undefined ? acc : reduce(xs, f, f(acc, x))

Underscore实现确实提供了一些其他的便利,虽然我猜这些是为了保持与本地Array.prototype.reduce的兼容性。我个人不会以这种方式实施减少,但这不是重点。

  1. Underscore传递迭代器和arr对回调函数的引用。
  2. Underscore允许您更改回调函数的上下文
  3. 这是一个支持这些其他功能的修订实施

    // our reduce version 2.0
    function reduce(collection, iterator, memo, context) {
      function loop(memo, i) {
        if (collection.length === i)
          return memo
        else
          return loop(iterator.call(context, memo, collection[i], i, collection), i + 1)
      }
      return loop(memo, 0)
    }
    

    您现在可以像上面一样使用它,它为回调提供了更多信息

    请注意

    我故意决定来实现Underscore减少的行为,允许您在没有初始值的情况下执行减少。支持此行为会导致代码不安全,并且首先应该永远不会将其变为Underscore。

答案 1 :(得分:0)

就像它:

function reduce(array, combine, start) {
  for (var i = 0; i < array.length; i++)
    start = combine(start, array[i]);
  return start;
}

console.log(reduce([1, 2, 3, 4], function(a, b) {
  return a + b;
}, 0));

链接参考:http://eloquentjavascript.net/05_higher_order.html

答案 2 :(得分:-1)

在这些答案的评论中,下划线的reduceArray.prototype.reduce之间存在很多混淆。两个注意事项:

  1. 下划线的reduce允许空集合且没有种子值。在这种情况下,它不会抛出错误,而是返回undefined。纳莫克让我确信这不安全。例如,_([]).reduce(function(a, b) { return a + b});应抛出错误或返回空列表。
  2. 下划线的reduce适用于对象和数组。
  3. 现在,到我原来的帖子:

    我实际上做了同样的事情 - 从头开始​​实施Underscore的关键功能 - 一段时间后,reduce可能是最棘手的。我认为reduce更容易与非功能性reduce进行交流(为此归功于naomik):

    function reduce(arr, func, seed) {
        var result = seed,
            len = arr.length,
            i = 0;
        for (; i < len; i++) {
            result = func(result, arr[i])
         }
         return result
     }
    

    Underscore的实现有点复杂,处理对象和数组,空集合和可选的种子值。它还使用each而不是for循环,因为它在样式上更具功能性。这是我对Underscore的reduce

    的实现
    var reduce = function(coll, func, seed) {
        // `isEmpty` (not shown) handles empty arrays, strings, and objects.
        // Underscore accepts an optional seed value and does not 
        // throw an error if given an empty collection and no seed.
        if (isEmpty(coll)) {
            return coll;
        }
        var noSeed = arguments.length < 3;
    
        // `each` (not shown) should treat arrays and objects
        // in the same way.
        each(coll, function(item, i) {
            if (noSeed) {
                // This condition passes at most once. If it passes,
                // this means the user did not provide a seed value.
                // Default to the first item in the list.
                noSeed = false;
                seed = item;
            } else {
                seed = func(seed, item, i);
            }
        });
    
        return seed;
    };