For an associative operation f
over the elements of array a
, the following relation should hold true: a.reduce(f)
should be equivalent to a.reduceRight(f)
.
Indeed, it does hold true for operations that are both associative and commutative. For example:
const a = [0,1,2,3,4,5,6,7,8,9];
const add = (a, b) => a + b;
console.log(a.reduce(add));
console.log(a.reduceRight(add));
However it doesn't hold true for operations that are associative but not commutative. For example:
const a = [[0,1],[2,3],[4,5],[6,7],[8,9]];
const concat = (a, b) => a.concat(b);
console.log(JSON.stringify(a.reduce(concat)));
console.log(JSON.stringify(a.reduceRight(concat)));
We need to flip the arguments of f
for reduceRight
to make them equivalent:
const a = [[0,1],[2,3],[4,5],[6,7],[8,9]];
const concat = (a, b) => a.concat(b);
const concatRight = (b, a) => a.concat(b);
console.log(JSON.stringify(a.reduce(concat)));
console.log(JSON.stringify(a.reduceRight(concatRight)));
This makes me believe that the native implementation of reduceRight
is wrong.
I believe that the reduceRight
function should be implemented as follows:
var REDUCE_ERROR = "Reduce of empty array with no initial value";
Array.prototype.reduceRight = function (f, acc) {
let { length } = this;
const noAcc = arguments.length < 2;
if (noAcc && length === 0) throw new TypeError(REDUCE_ERROR);
let result = noAcc ? this[--length] : acc;
while (length > 0) result = f(this[--length], result, length, this);
return result;
};
Since result
represents the previous value (right-hand side value), it makes sense to make it the second parameter of the function f
. The current value represents the left-hand side value. Hence, it makes sense to make the current value the first parameter of the function f
. This way, even for non-commutative associative operations, the aforementioned relation holds true.
So, my questions are:
- Doesn't it indeed make more sense for
reduceRight
to be implemented the way I did? - Why is the native
reduceRight
not implemented the way I did?
foldr
andfoldl
operate from different directions? That's the point of having both, and non-commutative operations will necessarily return different results. – Alannafoldl1 (++) xs == foldr1 (++) xs
isTrue
in Haskell. – Multicolored