2013-08-06 25 views
5

在处理seq中的每个元素时,我通常使用firstrest。 但是,这些将导致lazy-seq通过调用参数seq而失去其“懒惰”。我的解决方案一直使用(first (take 1 coll))(drop 1 coll)来代替lazy-seq s,而我认为drop 1就好了,我不是特别喜欢拨打firsttake来获得第一个元素。在clojure中获取惰性seq的第一个元素的习惯方法

有没有更习惯的方式来做到这一点?

回答

10

firstrest文档字符串说,这些功能呼吁他们的论点seq传达,传递一个seqable收集这本身不是一个序列,就像当你不必调用seq自己的想法,说,一个矢量或集合。例如,

(first [1 2 3]) 
;= 1 
如果 first没有在它的参数调用 seq

是行不通的;你必须说

(first (seq [1 2 3])) 

而不是,这将是不方便的。

takedrop也会在它们的参数上调用seq,否则就不能像上面解释的那样在向量等上调用它们。事实上,所有标准seq集合都是如此 - 那些不直接调用seq的集合是建立在低级别组件上的。

这绝不会影响懒惰seqs的懒惰。作为first/rest调用的结果而发生的强制/实现是获得所请求结果的可能最小量。 (这取决于参数的类型;如果它实际上并不是懒惰的,那么调用first时不会涉及额外的实现;如果它部分是懒惰的 - 也就是分块 - 则会有一些额外的实现实现(一次最多可以计算32个初始元素);如果完全惰化,则只计算第一个元素。)

显然first当传递一个懒惰的seq时,必须强制实现它的第一个元素 - - 这就是整个观点。 rest实际上有些懒惰,因为它实际上并不强制实现seq的“休息”部分(这与next相对,其基本相当于(seq (rest ...)))。它确实强迫第一个元素被实现,以便它可以立即跳过它,这是一个有意识的设计选择,它可以避免不必要的lazy seq对象分层并保留原始seq的头部;你可以说类似(lazy-seq (rest xs))这样的东西来推迟这个初始实现,代价是保持到xs,直到实现懒惰的seq包装器。

+0

谢谢你。我认为这是把我扔掉的大块头和完全懒惰之间的区别。特别是当我第一次使用lazy-seq时,我认为无论我在做什么,在分块时肯定都会产生问题,所以我在之后就坚持下去了。 – robertjlooby

+0

是的,如果人们忘记它,分块会导致意想不到的结果。 'take'在其实现中同时使用'first'和'rest',而'drop'建立在'rest'上,所以它们不起作用。非时间运行是可能的,但只有在这种意义上,可以引起分块序列顶层的转换才能被一次评估一个元素;底层的分块式seq将始终完全实现它的块。 (要做到这一点,就是把chunked seq包装在一个unhking seq中,也许用'(reify clojure.lang.ISeq ...)'用原始seq在闭包中生成。) –

+0

你确定'take'/'drop'也实际调用'seq'?只需在repl中尝试一下,并用'(range)','(取1(range))','(drop 1(range))'调用'class',就会给出'clojure.lang.LazySeq'。调用'(rest(range))','(seq(range))'给出'clojure.lang.ChunkedCons'。 – robertjlooby

相关问题