在clojure中获取懒惰seq的第一个元素的惯用法

时间:2013-08-06 02:37:42

标签: clojure lazy-sequences

处理seq中的每个元素时,我通常使用firstrest。 但是,通过在参数上调用lazy-seq,这些会导致seq失去“懒惰”。我的解决方案是在使用(first (take 1 coll)) s时使用(drop 1 coll)lazy-seq,虽然我认为drop 1很好,但我并不特别喜欢致电firsttake以获取第一个元素。

是否有更惯用的方式来做到这一点?

1 个答案:

答案 0 :(得分:10)

firstrest的文档字符串表示这些函数在其参数上调用seq来表达您在传递时不必自己调用seq的想法在一个seqable集合中,它本身不是一个seq,比如一个向量或集合。例如,

(first [1 2 3])
;= 1
如果first未在其参数上调用seq

将无效;你不得不说

(first (seq [1 2 3]))
相反,这将是不方便的。

takedrop都会在其参数上调用seq,否则您无法在向量等上调用它们,如上所述。事实上,所有标准seq集合都是如此 - 那些不直接调用seq的集合构建在较低级别的组件上。

这绝不会损害懒惰的seqs的懒惰。由于first / rest调用而发生的强制/实现是获得请求结果的最小量。 (多少取决于参数的类型;如果它实际上不是懒惰的,first调用中没有额外的实现;如果它部分是懒惰的 - 那就是,chunked - 那里将是一些额外的实现(一次最多可以计算32个初始元素);如果它是完全惰性的,则只计算第一个元素。)

显然first,当传递一个懒惰的seq时,必须强制实现它的第一个元素 - 这就是重点。 rest实际上有点懒,因为它实际上并没有强制实现seq的“rest”部分(与next形成对比,这基本等同于(seq (rest ...))) 。事实上,它确实强制第一个元素被实现,以便它可以立即跳过它是一个有意识的设计选择,它避免了懒惰的seq对象的不必要的分层并保持原始seq的头部;你可以说像(lazy-seq (rest xs))之类的东西,甚至推迟这个初步实现,代价是坚持xs,直到实现了懒惰的seq包装器。