这个循环的函数式替代方案是什么?

4
nums = [2 5 3 7]
result = []
result.push {x:nums[0]}
for n in nums.slice(1)
    result.push {n:n + result[-1].x}
log result
# [{x:2} {x:7} {x:10} {x:17}]

使用函数 map 在功能上难以表达,因为每个元素都依赖于前一个元素。对于这个算法来说,什么是正确的函数式解决方案呢?


1
循环体不应该是 result.push {x:n + result[-1].x} 吗? - Ted Hopp
我不懂coffeescript,但我知道map只是fold的一个特例,而fold是更通用的循环。使用fold,你可以做到你所要求的。 - kqr
在JS中,map/filter()执行的操作reduce()无法实现;它允许调用时设置_this_,从而实现可重用函数,而reduce()无法实现。例如:function gt(n){return n>this;} [1,2,3,4,5].filter(gt, 3); - dandavis
6个回答

18

我知道的最简单方法避免了性能下降的闭包、变量、额外的函数开销和全局变量:

result= [2, 5, 3, 7].map(function(a){ return { x: this[0]+=a }; }, [0]);

JS 提供了很少使用的第二个 .map() 参数,用于在迭代之间存储所需的任何状态。

这可能再简单不过了,但我不知道咖啡是什么,抱歉...

编辑:制作了一个双语(js+cs)演示:http://pagedemos.com/maptranforms/


1
不错!我喜欢这个比我的答案更好。 - Ted Hopp
2
result = [2,5,3,7].map ((a) -> x: @[0] += a), [0] - mutil

8
您所描述的是一种扫描方法:一种同时返回中间结果的折叠方法。使用 prelude.ls 中的 scan1 方法:
nums = [2 5 3 7]
scan1 (+), nums |> map ((num) -> { x : num })
# => [{x: 2}, {x: 7}, {x: 10}, {x: 17}]

如果你只需要数组中的数据项的总和(包括每个元素的中间结果),那么可以完全省略 map 操作,直接这样写:
scan1 (+), [2 5 3 7] # => [2, 7, 10, 17]

scan1 documenation.


3

你需要在某个地方保存一些状态信息。以下是一个JavaScript闭包,它可以完成这项工作:

var nums = [2, 5, 3, 7];
var result = nums.map(
    (function() {
        var lastX = 0;
        return function(n) {
            return {x : (lastX += n)};
        }
     }())
);
// result is [{x:2} {x:7} {x:10} {x:17}]

2

dandavis 在 Coffeescript 中的答案如下:

nums.map ((x)->{x: @[0] += x}), [0]

一种可能更加清晰的变体。
nums.map ((x)->{x: @accum += x}), {accum:0}

使用coffeescript理解(并且使用相同的累加器思想)。
accum = 0; z = ({x: accum += i} for i in nums)

2

JavaScript

保持一个计数器,每次只需添加新数字即可:

var nums = [2,5,3,7];

var createObject = function(nums){ 
    var result = [],
        total = 0;

    for(var i = 0; i < nums.length; i++){
        total += nums[i]; 
        result.push({"x": total});
    }

    return result;
};

JSFIDDLE


这只是将OP的代码直接翻译成JavaScript,它并没有用函数式风格替换循环。 - Ted Hopp

1
map (-> {x:it}) <| (fold ((acc, a) -> acc ++ [a + ((last acc) ? 0)]), []) <| [2, 5, 3, 7]

1

代码有点长,但我更喜欢去掉条件语句

map (-> {x:it}) <| drop 1 <| (fold ((acc, a) -> acc ++ [a + (last acc)]), [0]) <| [2 5 3 7]`
- lab419

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接