怎样用理解使用 Transducer?

题叶
  • 17.3k

之前看过 Clojure 作者的演讲:
http://www.tudou.com/programs/view/5y_hDVID7gQ/
http://www.tudou.com/programs/view/PqdnO8uU6UU/
现在又看了一个 JavaScript 的教程:
http://phuu.net/2014/08/31/csp-and-transducers.html
大意 Transducer 是从 reduce 抽象出来的跟数据结构无关的序列操作的新办法(?), 而且还能作为函数任意复合(?), 不知道是不是这个意思...

有没有同学熟悉这方面, 而且能用生动的例子讲解一下原理, 特别还有怎么用的?

回复
阅读 5k
2 个回答

说多了都是痛, 一个例子就够了

首先是简单的概念
reduce 接收一个reducing函数 和一个collection返回值, 比如+是reducing函数
transducer 接收一个reducing函数, 返回一个新的reducing函数, (map inc)返回transducer, 而不是柯里化

所以这俩组合就变成

clojure(reduce ((map inc) +) [1 2 3]);;直接用transducer返回的新reducing函数reduce

如果没有transducer是这样的

clojure(reduce + (map inc [1 2 3])) ;;先弄出个新数组, 再reduce, 多出来一步,慢, 而且不lazy

想看稍微长一点的解释可以看看 我这篇文章, 虽然写给js dev的, 应该更好理解才是.

Transducer 据说是在开发 core.async 的过程中,Rich Hickey 发现又在重新实现 map, filter, partition 等操作,这些操作与 clojure/core 中的 sequence 操作极为类似,只不过对象是异步通道而不是序列。因此他重新思考后引入了 transducer 概念,并重新实现了这些操作(1.7.0-alpha)。因此,transducer 是未来的序列操作的基础。你仍然可以象过去一样使用序列操作,对我来说现在获得的额外好处有:

  • 可以用同样的函数来处理异步问题和同步问题。
  • 由于 transducer 是函数间的组合关系,它不用产生中间的数据对象,因此在不需要 lazy 的场合会有更好的运行效率。

我们过去使用序列时,一般都在写转换过程(transform),如 (map inc [0 1 2]) 其中的 inc 就是转换过程。现在我们可以:(def do-inc (map inc)) 来将对每个元素进行 inc 操作 提取出来。还可以:(def do-inc (comp (filter even?) (map inc))) 将连续的转换过程方便地组合,注意这个操作现在可以用在任何可以被 reduce 的对象上,包括但不限于序列、异步通道。

更重要的是,map, filter 这些操作不过是 transducer,它的作用是用 transform 参数来生成函数,我们可以写作自己的 transducer,而且可以是有状态的 transducer,这让大设计更加容易。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏