组合两个相同大小的数组并返回总和

Joh*_*ohn 1 javascript arrays functional-programming

在 Javascript(或任何其他具有类似函数语法的编程语言)中,如果我有两个相同大小的数组,例如 a = [1,2,3,4]b=[5,6,7,8],那么获得以下结果的最有效方法是什么: c=[6,8,10,12]

现在,我这样做:

a.map(function(x,i){ return x+b[i] })

但理想情况下我想要一个不涉及使用索引的解决方案。

Ori*_*iol 5

ES5 数组方法很棒,但旧for循环更快。

var sum = Array(a.length);
for(var i=0; i<a.length; ++i)
  sum[i] = a[i] + b[i];
Run Code Online (Sandbox Code Playgroud)


Tha*_*you 5

警告@dangor的答案改变了其中一个输入,在我看来这是非常糟糕的。我的回答将为您提供结果,而不会产生副作用。

Babel demo— 下面的代码是使用 ES6 编写的。如果您需要 ES5 代码,请查看 Babel REPL 中的右侧面板。


我首先推荐一个zip功能。该函数将采用两个数组并创建一个元组数组。

// zip :: [a] -> [b] -> [(a,b)]
let zip = (xs, ys) => {
  let iter = (zs, [x, ...xs], [y, ...ys]) =>
    (!x || !y) ? zs : iter(zs.concat([[x,y]]), xs, ys)
  return iter([], xs, ys);
};

let a = [1,2,3,4];
let b = [5,6,7,8];

zip(a,b); //=> [[1,5], [2,6], [3,7], [4,8]]
Run Code Online (Sandbox Code Playgroud)

map现在您可以在每个元组上使用

zip(a,b).map(t => t[0] + t[1]);
//=> [6,8,10,12]
Run Code Online (Sandbox Code Playgroud)

所以最后一点看起来有点令人讨厌,但如果我们继续构建一些功能更强大的构建块,我们可以清理它

如果我们注意到,在我们的映射函数中,我们所做的就是对数组的元素求和。我们可以通过创建函数来避免过于具体。

让我们首先看看如何对数组求和......

[1,2,3,4].reduce((x, y) => x + y, 0); //=> 10
Run Code Online (Sandbox Code Playgroud)

好的,我们首先可以改进的是闭包。该函数只是一个add函数。

let add = (x,y) => x + y;
[1,2,3,4].reduce(add, 0); //=> 10
Run Code Online (Sandbox Code Playgroud)

但是我们如何使用它来改进我们的原始代码呢?

// original code
zip(a,b).map(t => t[0] + t[1]);      //=> [6,8,10,12]

// improvement #1
zip(a,b).map(t => t.reduce(add, 0)); //=> [6,8,10,12]
Run Code Online (Sandbox Code Playgroud)

好吧,还没有太大的改进,而且在我们修复该功能之前它不会变得更好reduce。JavaScript 强制我们使用以下表示法调用reduce:obj.reduce(fn, i)。这种参数顺序有点糟糕,所以让我们解决这个问题

// improvement #2
let reduce = f => i => xs => xs.reduce(f, i);
zip(a,b).map(reduce(add)(0)); //=> [6,8,10,12]
Run Code Online (Sandbox Code Playgroud)

最后,我们可以定义一个函数,而不是reduce(add)(0)每次想要对数组求和时都使用sum

// improvement #3
let sum = reduce(add)(0);
zip(a,b).map(sum); //=> [6,8,10,12]
Run Code Online (Sandbox Code Playgroud)

现在都在一起了 ...

let zip = (xs, ys) => {
  let iter = (zs, [x, ...xs], [y, ...ys]) =>
    (!x || !y) ? zs : iter(zs.concat([[x,y]]), xs, ys)
  return iter([], xs, ys);
};

let add = (x,y) => x + y;
let reduce = f => i => xs => xs.reduce(f, i);
let sum = reduce(add)(0);

let a = [1,2,3,4];
let b = [5,6,7,8];

let result = zip(a,b).map(sum);

console.log(result);
//=> [6,8,10,12]
Run Code Online (Sandbox Code Playgroud)

当然,这会比其他解决方案产生更多的整体代码,但练习的重点是,当您使用此方法获得解决方案时,您现在可以将四个函数重新用于其他代码:zipaddreduce、而且sum都是非常多才多艺的;所有这些都是完整纯粹的

最后,这些函数的使用为我们提供了一种无点解决方案,它表现出比命令式方言更强的声明性方言。

我希望这能帮助您了解如何组装几个较小的函数来实现您的目标 - 所有这些都不必做愚蠢的事情,例如诉诸命令式for循环或屈服于鲁莽的副作用(例如改变输入)。

  • @BryanChen 这并不重要。首先,你将苹果与橙子进行比较;功能性答案不是强制性的,OP 正在寻找功能性答案。其次,这是零点几秒 vs 零点几秒。除非代码处于大容量循环中或其他情况,否则此类优化是没有意义的。更有效的关注点是代码的可维护性和可读性;您如何评价“zip(a,b).map(sum)”与“var sum = Array(a.length);” for(var i=0; i&lt;a.length; ++i) sum[i] = a[i] + b[i];` ? (2认同)