在处理seq
中的每个元素时,我通常使用first和rest。 但是,这些将导致lazy-seq
通过调用参数seq
而失去其“懒惰”。我的解决方案一直使用(first (take 1 coll))
和(drop 1 coll)
来代替lazy-seq
s,而我认为drop 1
就好了,我不是特别喜欢拨打first
和take
来获得第一个元素。在clojure中获取惰性seq的第一个元素的习惯方法
有没有更习惯的方式来做到这一点?
在处理seq
中的每个元素时,我通常使用first和rest。 但是,这些将导致lazy-seq
通过调用参数seq
而失去其“懒惰”。我的解决方案一直使用(first (take 1 coll))
和(drop 1 coll)
来代替lazy-seq
s,而我认为drop 1
就好了,我不是特别喜欢拨打first
和take
来获得第一个元素。在clojure中获取惰性seq的第一个元素的习惯方法
有没有更习惯的方式来做到这一点?
为first
和rest
文档字符串说,这些功能呼吁他们的论点seq
传达,传递一个seqable收集这本身不是一个序列,就像当你不必调用seq
自己的想法,说,一个矢量或集合。例如,
(first [1 2 3])
;= 1
如果
first
没有在它的参数调用
seq
是行不通的;你必须说
(first (seq [1 2 3]))
而不是,这将是不方便的。
take
和drop
也会在它们的参数上调用seq
,否则就不能像上面解释的那样在向量等上调用它们。事实上,所有标准seq集合都是如此 - 那些不直接调用seq
的集合是建立在低级别组件上的。
这绝不会影响懒惰seqs的懒惰。作为first
/rest
调用的结果而发生的强制/实现是获得所请求结果的可能最小量。 (这取决于参数的类型;如果它实际上并不是懒惰的,那么调用first
时不会涉及额外的实现;如果它部分是懒惰的 - 也就是分块 - 则会有一些额外的实现实现(一次最多可以计算32个初始元素);如果完全惰化,则只计算第一个元素。)
显然first
当传递一个懒惰的seq时,必须强制实现它的第一个元素 - - 这就是整个观点。 rest
实际上有些懒惰,因为它实际上并不强制实现seq的“休息”部分(这与next
相对,其基本相当于(seq (rest ...))
)。它确实强迫第一个元素被实现,以便它可以立即跳过它,这是一个有意识的设计选择,它可以避免不必要的lazy seq对象分层并保留原始seq的头部;你可以说类似(lazy-seq (rest xs))
这样的东西来推迟这个初始实现,代价是保持到xs
,直到实现懒惰的seq包装器。
谢谢你。我认为这是把我扔掉的大块头和完全懒惰之间的区别。特别是当我第一次使用lazy-seq时,我认为无论我在做什么,在分块时肯定都会产生问题,所以我在之后就坚持下去了。 – robertjlooby
是的,如果人们忘记它,分块会导致意想不到的结果。 'take'在其实现中同时使用'first'和'rest',而'drop'建立在'rest'上,所以它们不起作用。非时间运行是可能的,但只有在这种意义上,可以引起分块序列顶层的转换才能被一次评估一个元素;底层的分块式seq将始终完全实现它的块。 (要做到这一点,就是把chunked seq包装在一个unhking seq中,也许用'(reify clojure.lang.ISeq ...)'用原始seq在闭包中生成。) –
你确定'take'/'drop'也实际调用'seq'?只需在repl中尝试一下,并用'(range)','(取1(range))','(drop 1(range))'调用'class',就会给出'clojure.lang.LazySeq'。调用'(rest(range))','(seq(range))'给出'clojure.lang.ChunkedCons'。 – robertjlooby