函数式编程风格的增长数组循环

Fab*_*oli 5 javascript arrays functional-programming

迭代在 javascript 中迭代期间增长的数组的最聪明方法是什么?我想遍历所有添加的元素,甚至是在迭代过程中添加的元素。我想用函数式编程风格来做。

例如看这个代码

let a = [
	'x',
  'y'
]

let limit = 4  // limit the test

for (let i=0; i<a.length; i++) {
	console.log(a[i])
    limit--
    if(limit>0){
      a.push(a[i]+'-') 
    }
}

console.log(a)
Run Code Online (Sandbox Code Playgroud)

执行后得到

[
  "x",
  "y",
  "x-",
  "y-",
  "x--"
]
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用替代的“功能”模式,例如 forEach,则不会打印新添加的元素

[
  "x",
  "y",
  "x-",
  "y-",
  "x--"
]
Run Code Online (Sandbox Code Playgroud)

Ser*_*aev 5

正如鲁本斯正确地注意到的那样,函数式方法假定您的函数不会改变源数据,而是对其进行转换并返回新值。

在以下示例中,源数组保持不变,并transform通过递归调用自身并传递更新的参数来遍历它:

function transform(array, limit, acc = []) {
    if (limit === 0 || array.length === 0) {
        return acc;
    }

    const head = array[0];
    const tail = array.slice(1);
    return transform(tail.concat(head + "-"), limit - 1, acc.concat(head));
}

transform(["x", "y"], 5).forEach(x => console.log(x))
Run Code Online (Sandbox Code Playgroud)

结果数组受limit参数限制,这limit = 5意味着结果中只有 5 个元素。

如果我们查看在transform每次迭代时传递给函数的参数,我们会看到limitacc( 的缩写accumulator)接收新元素时,它会变小:

transform(["x",   "y"],    5, [])
transform(["y",   "x-"],   4, ["x"])
transform(["x-",  "y-"],   3, ["x", "y"])
transform(["y-",  "x--"],  2, ["x", "y", "x-"])
transform(["x--", "y--"],  1, ["x", "y", "x-", "y-"])
transform(["y--", "x---"], 0, ["x", "y", "x-", "y-", "x--"])
Run Code Online (Sandbox Code Playgroud)

limit达到零时,acc作为结果返回值。请注意,没有数据发生变异。

值得一提的是transformcall 处于尾部位置,并且由于 ES6优化了尾部调用(在严格模式下),我们不必担心调用堆栈的增长。

另请注意,上面的代码仅说明了这个想法。应该小心使用它,因为它在大量数据上可能会非常慢,因为每次迭代都会有sliceing 和concating 数组。

尾调用优化更新:尽管尾调用优化是 ES6 规范的一部分,但浏览器并不广泛支持它,因此在使用递归时需要谨慎。请参阅兼容性表