2017-07-04 27 views
1

什么是最好的方式来迭代在JavaScript中迭代期间增长的数组?我想迭代所有添加的元素,甚至在迭代期间添加的元素。我想在功能性编程风格上做到这一点。函数式编程风格增长数组循环

例如看到这个代码执行了

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

 
let limit = 4 // limit the test 
 

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

 
console.log(a)

[ 
    "x", 
    "y", 
    "x-", 
    "y-", 
    "x--" 
] 

但是,如果我尝试用另一种 “funcional” 模式一样的forEach新增加的元素未印刷

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

 
let limit = 4 // limit the test 
 

 
a.forEach((e) => { 
 
    console.log(e) 
 
    limit-- 
 
    if(limit>0){ 
 
     a.push(e+'-') 
 
    } 
 
}) 
 

 
console.log(a)

+0

第一回路检查长度每次迭代。随着长度的增长迭代继续。使用'forEach()'只能迭代原始数组长度 – charlietfl

+0

a.forEach((e))说:对于每个元素,名为e,do:/:你的代码,这不是范围 –

+1

_“什么是最聪明的如何迭代在迭代过程中增长的数组?在迭代过程中,JavaScript?“_从功能角度来看:迭代不增长的数组。 – ftor

回答

3

正如Rubens正确地注意到的,函数方式假定你的函数不会改变源数据,而是改变它并返回新的值。

在下面的例子源阵列被保持原封不动,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))

所得阵列由limit说法,limit = 5限于意味着结果中只有5个元素会结束。

如果我们看一下传递给每个迭代transform函数的参数,我们会看到limit变小而acc(简称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--"]) 

limit达到零,因此返回acc值。请注意,没有数据发生变异。

值得一提的是,transform呼叫处于尾部位置,而自ES6 optimizes tail calls(严格模式),我们不必担心增加呼叫堆栈。

还要注意,上面的代码只是说明了这个想法。它应该小心使用,因为它可能对大量数据非常缓慢,因为每次迭代时都会有数组。

2

官能方式,要做到这一点,最好的办法是使用观测量(响应式编程)。这个数组可以变成一个“生产者”(Observable),只要这个集合增长,它就会发出值,所以即使这个集合随时间增长,你也可以安全地做任何你想要的事情。 也许这是“超过工程”只是“记录”一些值,但取决于你的真实问题,它可以是一个很好的解决方案。

您可以查看文档here

但是,如果你看看你的代码,在编程功能,你不会改变当前数组(使用引用),而是创建一个完整的新数组。在功能性编程中,变量趋于“只读”,遵循“不变性”概念。 JS还没有本地化,you can use libs for that tough。 无论如何,我做了这个代码的一个例子:

function dashAccumulator(arr, times) { 
 
    if(times) { 
 
    const dashed = arr.map(val => `${val}-`); 
 
    return [...arr, ...dashAccumulator(dashed, --times)]; 
 
    } 
 
    else return arr; 
 
} 
 

 
const a = ['x', 'y']; 
 
const iter = 4; 
 
console.log(dashAccumulator(a, iter));

地图将创建一个新的阵列,也,返回时,我创建基于另一个在地图结果的值和“原始”数组中的上下文。这样你不需要改变任何引用并保持相同的最终结果。

1

我想在功能性编程风格上做到这一点。

在函数式编程中,数组不增长。然而,使用懒惰,你可以遍历不存在的元素,基本上是无限的流。例如,在Haskell:

> let a = ["x", "y"] ++ map (++"-") a 
> --        ^reference to the list itself 
> take 5 a 
["x","y","x-","y-","x--" 

什么是迭代在JavaScript迭代过程中,生长的数组最聪明的方法是什么?

我会使用类似的方法,但当然,你需要的是变异的阵列的辅助功能:

function takeMapPrev(fn, arr, limit) { 
    if (!arr.length) throw new RangeError("must start with something"); 
    var i = 0; 
    while (arr.length < limit) 
     arr.push(fn(arr[i++])); 
    return arr; 
} 

console.log(takeMapPrev(e => e+'-', ['x','y'], 5)); // ["x","y","x-","y-","x--"]