从头开始创建下划线 reduce 函数
Creating the underscore reduce function from scratch
我正在努力创建自己的回调函数和高阶函数组。我坚持复制下划线减少功能或 ._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);
}
有点像:
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));
在这些答案的注释中,Underscore的reduce
和Array.prototype.reduce
之间出现了很多混淆。两个注意事项:
- Underscore 的
reduce
允许空集合且没有种子值。在这种情况下,它不会抛出错误,而是 returns undefined
。 Naomik 让我相信这是不安全的。例如 _([]).reduce(function(a, b) { return a + b});
应该抛出一个错误或者 return 一个空列表。
- Underscore 的
reduce
适用于对象和数组。
现在,回到我原来的post:
我实际上做了同样的事情——从头开始实现 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;
};
一个简单的递归函数就可以了
// 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 的兼容性。我个人不会以这种方式实施 reduce,但这不是重点。
- Underscore 将迭代器和 arr 引用传递给回调函数。
- 下划线允许您更改回调函数的上下文
这是支持这些其他功能的修改后的实现
// 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 的 reduce 行为,允许您在没有初始值的情况下执行归约。支持这种行为会导致代码不安全,一开始就不应该将其放入 Underscore 中。
我正在努力创建自己的回调函数和高阶函数组。我坚持复制下划线减少功能或 ._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);
}
有点像:
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));
在这些答案的注释中,Underscore的reduce
和Array.prototype.reduce
之间出现了很多混淆。两个注意事项:
- Underscore 的
reduce
允许空集合且没有种子值。在这种情况下,它不会抛出错误,而是 returnsundefined
。 Naomik 让我相信这是不安全的。例如_([]).reduce(function(a, b) { return a + b});
应该抛出一个错误或者 return 一个空列表。 - Underscore 的
reduce
适用于对象和数组。
现在,回到我原来的post:
我实际上做了同样的事情——从头开始实现 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;
};
一个简单的递归函数就可以了
// 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 的兼容性。我个人不会以这种方式实施 reduce,但这不是重点。
- Underscore 将迭代器和 arr 引用传递给回调函数。
- 下划线允许您更改回调函数的上下文
这是支持这些其他功能的修改后的实现
// 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 的 reduce 行为,允许您在没有初始值的情况下执行归约。支持这种行为会导致代码不安全,一开始就不应该将其放入 Underscore 中。