对于数组f
的元素的关联操作a
,以下关系应该成立:a.reduce(f)
应该等同于a.reduceRight(f)
。
实际上,它确实适用于相关和可交换的操作。对于 例如:
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;
}
但是,对于关联但不可交换的操作,它不适用。例如:
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;
}
我们需要为f
翻转reduceRight
的参数,使它们等效:
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
的原生实现是错误的。
我认为reduceRight
函数应按如下方式实现:
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;
};
由于right
表示先前的值(右侧值),因此将其作为函数f
的第二个参数是有意义的。当前值表示左侧值。因此,将当前值作为函数f
的第一个参数是有意义的。这样,即使对于非交换关联操作,上述关系也成立。
所以,我的问题是:
reduceRight
以我的方式实施是否确实更有意义?reduceRight
没有像我那样实现?答案 0 :(得分:4)
reduceRight
以我的方式实施是否确实更有意义?
也许。但是,JavaScript 数组迭代器并非来自纯函数式编程背景。
为什么本地
reduceRight
没有像我那样实现?
因为具有相同的参数顺序更简单(更容易记住),所以累加器始终是第一个。
数组上的基本操作是reduce
,它一如既往地从0迭代到n-1。只有在Haskell中使用递归构建的列表foldr
才更有意义(具有build
二元性,在无限列表上懒得工作......)。请注意命名不是reduce
+ reduceLeft
...
然后reduceRight
不会反转折叠操作,它只是反转 迭代顺序。这也是文档和教程中常见的解释,例如 The Definitive Guide :
reduceRight()
的工作原理与reduce()
类似,不过它会从最高处理数组。
对于JS 1.8,first implementation中reduce
/ reduceRight
Bug 363040 Mozilla's array extras的notes of Dave Herman也采用了这种方法:它只是以“结束”开头否定了步骤值。
ES4规范的missing thisObject
parameter遵循这一思路。它确实提到了Haskell,但整个文档根本没有处理callback
的参数顺序。也许在Haskells不常见的语法或规范类型名称中丢失了不同的顺序,因此两个签名都以(a -> b -> …
开头。更多讨论进入了got into the EcmaScript spec。
一些相关的摘录:
[方法]的好处:
- 就像Python =&gt; Python社区mindshare
- 折叠的完整普遍性(左)
- 但也是简单的情况,其中第一个元素是基础 元素,更简单
我猜大多数人都会发现从左到右的版本减少更多 直观,因为它们通常从左到右迭代数组。 加上这就是Python的作用。
我认为提供reduceRight也很重要,
因为不是每个操作都是关联的,有时人们需要 从右到左。
最后,这就是{{3}}:
数组附加内容 :按照FF当前支持的方式进行指定