【问题标题】:Idiomatic way to get first element of a lazy seq in clojure在clojure中获取惰性序列的第一个元素的惯用方法
【发布时间】:2013-08-06 21:58:34
【问题描述】:

在处理seq 中的每个元素时,我通常使用firstrest。 然而,这些将导致 lazy-seq 通过在参数上调用 seq 来失去其“惰性”。我的解决方案是在使用lazy-seqs 时使用(first (take 1 coll))(drop 1 coll) 代替它们,虽然我认为drop 1 很好,但我并不特别喜欢打电话给first 和@987654331 @ 获取第一个元素。

有没有更惯用的方法来做到这一点?

【问题讨论】:

    标签: clojure lazy-sequences


    【解决方案1】:

    firstrest 的文档字符串说这些函数在它们的参数上调用 seq 以传达这样一个想法,即当传入一个不在本身就是一个序列,比如一个向量或集合。例如,

    (first [1 2 3])
    ;= 1
    

    如果 first 没有在其参数上调用 seq 将不起作用;你不得不说

    (first (seq [1 2 3]))
    

    反而会很不方便。

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

    这绝不会削弱惰性序列的惰性。由于first / rest 调用而发生的强制/实现是获得请求结果的最小量。 (多少取决于参数的类型;如果它实际上不是惰性的,则first 调用中不涉及额外的实现;如果它是部分惰性的 - 即分块 - 将会一些额外的实现(一次最多计算 32 个初始元素);如果它是完全惰性的,则只会计算第一个元素。)

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

    【讨论】:

    • 谢谢。我认为这是让我失望的是分块与完全懒惰之间的区别。特别是当我第一次使用lazy-seq时,我认为我所做的任何事情在分块时一定会引起问题,所以在那之后我就一直坚持下去。
    • 是的,如果忘记分块,可能会导致意想不到的结果。 take 在其实现中同时使用firstrest,而drop 是基于rest 构建的,因此它们没有帮助。取消分块是可能的,但仅限于可以导致分层在分块序列之上的转换一次评估一个元素;底层分块 seq 将始终完全实现其块。 (这样做的方法是将分块的 seq 包装在一个未分块的 seq 中,可能使用 (reify clojure.lang.ISeq ...) 生成,原始 seq 在一个闭包中。)
    • 你确定take/drop 也真的打电话给seq?只需在 repl 中试一试,并用(range)(take 1 (range))(drop 1 (range)) 调用class,都给clojure.lang.LazySeq。使用(rest (range))(seq (range)) 调用会得到clojure.lang.ChunkedCons
    • takedrop 将它们的主体包裹在 lazy-seq 中,因此它们会保留整个输入集合,直到您真正意识到这个包装器序列。一旦您意识到这一点,生成的 seq 将由涉及对输入集合的显式调用 seq 的代码生成(原因在答案中解释),以及对 firstrest 的调用。事实上,在之前的评论中,我打算在第二句话中提到对seq 的调用,但不知何故我忘记了,现在我无法编辑......哦,好吧,我在答案中做到了。详情请参阅(source take)(source drop)
    • 所以实际上(drop 1 xs) 几乎等同于(lazy-seq (rest xs)),相关成本相同。请参阅 4 月的这个 SO 问题和我在那边的回答,了解使用这种包装器可能产生的不良结果的示例(当然,这只是要记住的事情,而不是在真正有用时避免使用此类包装器的理由): Clojure head retention.
    猜你喜欢
    • 1970-01-01
    • 2013-04-22
    • 2011-06-17
    • 1970-01-01
    • 2023-03-21
    • 2010-12-08
    • 2011-06-26
    • 2013-11-28
    • 2019-05-04
    相关资源
    最近更新 更多