在JavaScript中本地实现reduceRight是错误的 [英] Native implementation of reduceRight in JavaScript is wrong

查看:138
本文介绍了在JavaScript中本地实现reduceRight是错误的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于数组元素 a 的关联操作 f ,以下关系应该成立: a.reduce(f)应相当于 a.reduceRight(f)

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:

var a = [1,2,3,4,5,6,7,8,9,0];

alert(a.reduce(add) === a.reduceRight(add));

function add(a, b) {
    return a + b;
}

然而,对于关联但不可交换的操作,它不适用。例如:

However it doesn't hold true for operations that are associative but not commutative. For example:

var a = [[1,2],[3,4],[5,6],[7,8],[9,0]];

alert(equals(a.reduce(concat), a.reduceRight(concat)));

function concat(a, b) {
    return a.concat(b);
}

function equals(a, b) {
    var length = a.length;
    if (b.length !== length) return false;
    for (var i = 0; i < length; i++)
        if (a[i] !== b[i]) return false;
    return true;
}

我们需要翻转<$的参数c $ c> f for reduceRight 使它们等效:

We need to flip the arguments of f for reduceRight to make them equivalent:

var a = [[1,2],[3,4],[5,6],[7,8],[9,0]];

alert(equals(a.reduce(concat), a.reduceRight(concatRight)));

function concat(a, b) {
    return a.concat(b);
}

function concatRight(b, a) {
    return a.concat(b);
}

function equals(a, b) {
    var length = a.length;
    if (b.length !== length) return false;
    for (var i = 0; i < length; i++)
        if (a[i] !== b[i]) return false;
    return true;
}

这让我相信本机实现 reduceRight 错误。

This makes me believe that the native implementation of reduceRight is wrong.

我相信 reduceRight 函数应该按如下方式实现:

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) {
    var a = this, length = a.length;

    if (arguments.length < 2) {
        if (length !== 0) var right = a[--length];
        else throw new TypeError(REDUCE_ERROR);
    } else var right = acc;

    while (length !== 0) right = f(a[--length], right, length, a);

    return right;
};

由于正确表示先前的值(右) - 手边值),使它成为函数 f 的第二个参数是有意义的。当前值表示左侧值。因此,将当前值作为函数 f 的第一个参数是有意义的。这样,即使对于非交换关联操作,上述关系也成立。

Since right represents the previous value (right-hand side value), it makes sense to make it the second parameter of the function f. The current value represent 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.

所以,我的问题是:


  1. 以我的方式实施 reduceRight 确实更有意义吗?

  2. 为什么本地 reduceRight 没有像我那样实现?

  1. Doesn't it indeed make more sense for reduceRight to be implemented the way I did?
  2. Why is the native reduceRight not implemented the way I did?


推荐答案


reduceRight 以我的方式实现是否确实更有意义?

Doesn't it indeed make more sense for reduceRight to be implemented the way I did?

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

Maybe. However, the JavaScript array iterators do not come from a pure functional programming background.


为什么是原生 reduceRight 没有以我的方式实现?

Why is the native reduceRight not implemented the way I did?

因为它更简单(更容易记住) )具有相同的参数顺序,累加器总是第一个。

Because it's simpler (easier to remember) to have the same parameter order, the accumulator always first.

数组上的原始操作是 reduce ,一如既往地从0迭代到n-1。只有在Haskell中使用递归构建的列表 foldr 才更有意义(具有 build 二元性,在无限上很懒惰列出了......)。注意命名不是 reduce + reduceLeft ...

The primitive operation on arrays is reduce, which iterates from 0 to n-1 as always. Only in Haskell with its recursively-built lists foldr makes more sense (has the build duality, works well lazily on infinite lists…). Notice how the naming is not reduce+reduceLeft

然后 reduceRight 不会反转折叠操作,它只是反转 迭代顺序。这也是文档和教程中常见的解释,例如 The Definitive Guide

Then reduceRight does not inverse the fold operation, it just reverses the iteration order. This is also how it is typically explained in documentation and tutorials, for example in The Definitive Guide:


reduceRight()就像 reduce()一样,只不过它从最高处理数组。

reduceRight() works just like reduce(), except that it processes the array from highest.

此外首次实施 reduce / reduceRight (参见错误363040 Mozilla的数组附加 遵循这种方法:它只是以end开头并取消了步骤值。

Also the first implementation of reduce/reduceRight (see Bug 363040) in Mozilla's array extras for JS 1.8 followed this approach: It just flipped start with end and negated the step value.

Dave Herman的笔记他的ES4规范遵循了这一思路。它确实提到了Haskell,但整个文档根本没有处理回调的参数顺序。也许在Haskells不常见的语法或规范类型名称中丢失了不同的顺序,因此两个签名都以开头(a - > b - > ... 。更多的讨论进入了缺少 thisObject 参数

The notes of Dave Herman for the ES4 spec followed this line of thought. It does mention Haskell, but the whole document does not deal with the parameter order of the callback at all. Maybe the distinct order was lost in Haskells uncommon syntax, or the canonical type names so that both signatures began with (a -> b -> …. More discussion went into the missing thisObject parameter.

一些相关的摘录:


[方法]的好处:


  • 就像Python => Python社区mindshare

  • 已满折叠的一般性(左)

  • 但也是简单的情况,其中第一个元素是基础
    元素,更简单

  • just like Python => Python community mindshare
  • full generality of fold (left)
  • but also make the simple case, where the first element is the basis element, simpler

我猜大多数人会发现从左到右的版本减少更多

直观因为它们通常从左到右迭代数组。
加上Python的作用。

我认为它也是提供reduceRight很重要同样,

因为不是每个操作都是关联的,有时人们需要
从右到左。

最后,这就是参与了EcmaScript规范


数组附加功能:按照FF当前支持的方式进行指定

Array extras: Spec it the way it is currently supported in FF

这篇关于在JavaScript中本地实现reduceRight是错误的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆