Ruby 如何用 Ruby 实现类似 Clojure 中的 reducer?

lithium4010 · 2015年07月14日 · 最后由 msg7086 回复于 2015年07月15日 · 2115 次阅读

在《七周七并发模型》中的第二天(p57),书中介绍了一个 Clojure 的 reducer 的例子,并实现了一个 my-map 函数

=> (into [] (my-map (partial * 2) (my-map (partial + 1) [1 2 3 4])))
[4 6 8 10]

其中只进行了一次化简,化简函数是 (partial * 2) 和 (partial + 1) 组合后的函数。

Reducer: REF

A reducer is the combination of a reducible collection (a collection that knows how to reduce itself) with a reducing function (the "recipe" for what needs to be done during the reduction). The standard sequence operations are replaced with new versions that do not perform the operation but merely transform the reducing function. Execution of the operations is deferred until the final reduction is performed. This removes the intermediate results and lazy evaluation seen with sequences.

如何使用 ruby 实现一个类似的函数呢?

my_map = ->(fn, arr){
  # 怎么实现?
}

my_map.(->(x){x * 2}, my_map.(->(x){x + 1}, [1, 2, 3, 4])).into
# => [4, 6, 8, 10]

有意思,分明是 map,为什么叫做 reducer?

[1, 2, 3, 4].map {|partial| partial + 1}.map {|partial| partial * 2}

#1 楼 @yanhao 书上是用 clojure 的 reducer 来实现的 my-map

你这样写是会遍历两次数组吧?

Ruby 有 lazy (SICP 中讲到的 stream), 只遍历一次

[1, 2, 3, 4].each.lazy.map {|partial| partial + 1}.map {|partial| partial * 2}.to_a

但是 reducer 不仅是 lazy 的,还可以对 map 操作并行化 (fork threads), 然后在 fold 操作中序列化 (join threads), ruby 中没有对应

这是函数式才有的功能吧。 过程式语言的执行顺序会产生副作用,不能随便化简短路的。

比如 arr.map(&:a).map(&:b),在 ruby 里就要严格要求所有的 b 必须在所有的 a 之后执行,而绝对不能 a b a b 这样执行。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号