And*_*rea 7 collections clojure lazy-evaluation
我对Clojure很陌生,虽然我熟悉函数式语言,主要是Scala.
我想弄清楚在Clojure中对集合进行操作的惯用方法是什么.我特别感到困惑的是函数的行为,比如map.
在Scala中,非常注意制作,以便map始终返回相同类型的原始集合的集合,只要这有意义:
List(1, 2, 3) map (2 *) == List(2, 4, 6)
Set(1, 2, 3) map (2 *) == Set(2, 4, 6)
Vector(1, 2, 3) map (2 *) == Vector(2, 4, 6)
Run Code Online (Sandbox Code Playgroud)
相反,Clojure中,据我了解,大部分的操作,例如map或filter懒惰,甚至当上跃跃欲试的数据结构进行调用.这有着奇怪的结果
(map #(* 2 %) [1 2 3])
Run Code Online (Sandbox Code Playgroud)
懒惰列表而不是矢量.
虽然我更喜欢懒惰的操作,但我发现上述情况令人困惑.实际上,向量保证了列表没有的某些性能特征.
假设我使用上面的结果并追加到它的末尾.如果我理解正确,结果不会被评估,直到我尝试追加它,然后它被评估,我得到一个列表而不是一个向量; 所以我必须遍历它以追加到最后.当然我之后可以将它变成一个矢量,但这会变得混乱而且可以被忽略.
如果我理解正确,map是多态的并且实现它不会是一个问题,因此它返回向量上的向量,列表上的列表,流上的流(这次使用惰性语义)等等.我想我对Clojure及其成语的基本设计缺失了一些东西.
对clojure数据结构的基本操作不会预先反映结构的原因是什么?
在Clojure中,许多函数都基于Seq抽象.这种方法的好处是你不必为每个不同的集合类型编写一个函数 - 只要你的集合可以被视为一个序列(带有头部和可能是尾部的东西),你可以将它用于所有seq函数.采用seqs和输出seqs的函数比可将其用于某种集合类型的函数更具可组合性,因此可重用.在seq上编写自己的函数时,你不需要处理特殊情况,例如:如果用户给了我一个向量,我必须返回一个向量等.你的函数在seq管道里面和其他任何函数一样好. seq功能.
map返回lazy seq的原因是设计选择.在Clojure中,懒惰是许多这些功能结构的默认设置.如果您想要其他行为,例如没有中间集合的并行性,请查看reducers库:http://clojure.com/blog/2012/05/08/reducers-a-library-and-model-for-收集,processing.html
就性能而言,map总是必须在一个集合上应用一个函数n次,从第一个元素到最后一个元素,因此它的性能总是为O(n)或更差.在这种情况下,矢量或列表没有区别.懒惰会给你带来的好处是你只会消耗列表的第一部分.如果必须在地图输出的末尾附加某些内容,则向量确实更有效.mapv在这种情况下,你可以使用(在Clojure 1.4中添加):它接收一个集合并输出一个向量.我会说,如果你有充分的理由,只关心这些性能优化.大部分时间都不值得.
在这里阅读有关seq抽象的更多信息: http://clojure.org/sequences
另一个在Clojure 1.4中添加的向量返回高阶函数是filterv.