对于在f数组元素上的关联操作a,以下关系应成立:a.reduce(f)应等效于a.reduceRight(f)
确实,它对于关联和可交换的运算都适用。对于
例:

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));

但是,对于关联但非可交换的操作,它并不成立。例如:

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)));

我们需要将f的参数翻转为reduceRight以使其等效:

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)));

这使我相信reduceRight的本机实现是错误的。
我相信reduceRight函数应按以下方式实现:
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;
};
由于result表示先前的值(右侧值),因此使其成为f函数的第二个参数是有意义的。当前值表示左侧值。因此,使当前值成为函数f的第一个参数是有意义的。这样,即使对于非可交换的关联运算,上述关系也成立。
因此,我的问题是:
  • 以我的方式实现reduceRight确实更有意义吗?
  • 为什么本地reduceRight没有像我那样实现?
  • 最佳答案



    也许。但是,JavaScript数组迭代器并非来自纯函数式编程背景。



    因为具有相同的参数顺序更容易(更容易记住),所以累加器始终是第一个。

    数组上的原始操作是reduce,它一如既往地从0迭代到n-1。只有在具有递归构建列表的Haskell中,foldr才有意义(具有build对偶性,可以在无限列表上懒惰工作)。注意命名不是reduce + reduceLeft

    然后reduceRight不会反转折叠操作,它只是反转迭代顺序。这也是文档和教程(例如《权威指南》)中通常解释的方式:



    同样,对于JS 1.8,first implementationreduce / reduceRightBug 363040(请参阅Mozilla's array extras)也遵循这种方法:它只是从end开始翻转而否定step值。

    ES4规范的notes of Dave Herman遵循了这一思路。它确实提到了Haskell,但是整个文档根本不处理callback的参数顺序。可能是因为Haskells不常见的语法或规范的类型名丢失了唯一顺序,所以两个签名都以(a -> b -> …开头。 missing thisObject parameter进行了更多讨论。

    一些相关摘录:



    最后,这就是got into the EcmaScript spec:

    10-08 10:54