【问题标题】:Why Clojure's reverse function returns a non-lazy sequence?为什么 Clojure 的反向函数返回一个非惰性序列?
【发布时间】:2011-11-20 22:09:37
【问题描述】:

为什么 Clojure 的反向函数的设计者认为返回的序列不是惰性序列?
Clojure 通常包含惰性序列。

【问题讨论】:

    标签: clojure lisp lazy-evaluation


    【解决方案1】:

    当然是因为根据定义,为了反转一个序列,您必须知道另一端是什么,以便返回将成为反转集合中的第一项的内容。

    因此,序列必须是有限的,并且您必须对其进行评估才能使用其末尾的内容。

    附录:

    反向作为无限序列没有意义,(尽管可以公平地说无限序列并不总是懒惰的先决条件)。

    如果您要反转一个集合,那么您已经将它加载到内存中;不需要计算。

    【讨论】:

    • 什么?你的意思是你的电脑只能及时向前运行?!荒谬的! (;-))
    • 我明白你的意思,但也许我遗漏了一些东西。是的,序列应该有一个结尾才能反转,但是为什么反转函数会返回一个非惰性结果?
    • @Chiron 不过都已经看完了,没必要再耽搁了。
    • 来自 Clojure 的乐趣:“Clojure 中惰性的主要优点是它可以防止在计算期间完全实现中间结果”……“惰性保证序列不会在内存中完全实现在任何给定的步骤”。在逆转的情况下,我们没有中期结果。使用 reduce 和 conj 一步完成。
    • >>> 例如(reduce conj () '(1 2 3)) => (3 2 1)
    【解决方案2】:

    rseq 返回一个以相反顺序遍历集合的 seq。它仅适用于 可逆 集合,例如矢量、排序集和排序图。

    reverse 将反转任何有限序列,但这样做的代价是遍历所有项目并将它们附加到列表中。它被实现为(reduce conj () coll)。这显然不是懒惰的。

    【讨论】:

    • 这解释了“为什么”是如何实现的。它没有回答为什么选择这个实现,我认为这是真正的问题。
    • @ivant 选择这个实现是因为它的效率。您可以构建一个惰性版本的 reverse 遍历输入 seq 多次直到 [(count-1)th, (count-2)th, (count-3)th, ...] 项,因为惰性 seq 被消耗。但是一次性建立一个列表并返回它更有意义。对于可逆的集合,你当然应该使用 rseq。
    【解决方案3】:

    进一步Scott's answer

    reverse 函数逐个元素地减少其序列参数。如果我们可以让reverse 真正变得懒惰,我们可以对其他归约做同样的事情。所以我们写一个懒惰版的reduce

    (defn lazy-reduce [f init coll]
      (lazy-seq
       (if (seq coll)
         (lazy-reduce f (f init (first coll)) (rest coll))
         init)))
    

    顺便说一句,这只适用于序列。

    但它有效:

    (lazy-reduce conj () (range 5))
    ; (4 3 2 1 0)
    

    如果我们从不接触结果,则不会计算任何内容。我们赢了。但是一旦我们接触到第一个元素,整个序列就实现了,就像用掉线编织一样解开。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    • 2011-02-26
    • 2016-06-30
    相关资源
    最近更新 更多