【问题标题】:Holding onto the head of a sequence抓住序列的头部
【发布时间】:2010-02-06 18:45:27
【问题描述】:

阅读最近的一个问题,我确定了正在讨论的功能

(def fib-seq
    (lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))

作为抓住序列的头部,但我重新阅读我的答案时突然想到我已经掩盖了细节,就像它们很明显一样,所以我回去澄清并提出不足。我知道 fib-seq 是一个 var,只要它在它周围,它就会保存序列中的所有元素,但我完全不清楚序列是如何被保持的确切机制。任何澄清将不胜感激。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    基本上,常规的 GC 规则适用...序列只是一个对象,持有它的头部意味着持有对该对象的引用。这需要在内存中保存尽可能多的序列,因为 Clojure 序列是缓存的。

    (下面有更详细的解释——请参阅粗体片段了解它的要点...... ;-))

    Clojure 中的“序列”是一个实现 ISeq 接口的对象。这提供了提取序列的第一个元素和序列的其余部分(另一个实现 ISeq 的对象)的方法。作为一个关键细节,这些不仅要计算正确的对象(序列的第一个/其余部分)并将其返回给调用者,而且还要将计算值缓存在内存中,以便任何后续请求更快——而且,更多重要的是,这样可以保证对序列中相同元素的所有请求都返回相同的值,即使 ISeq 是在某个时刻发生变化的可变 Java 对象之上生成的。 (请注意,这对于 Clojure 序列的不可变语义绝对至关重要。)

    另一方面,Var 是一个容器,粗略地说,是一个指向某个 Java 对象的“指针”。如果这恰好是一个 ISeq,那么只要 Var 本身没有被垃圾回收(如果它是当前存在的命名空间中的顶级 var,显然永远不会这样)或反弹,ISeq 本身不会被垃圾收集,特别是它用于缓存第一个/其余序列的内存不会被释放

    至于序列的其他元素:绑定到 Var 的 ISeq 的“其余部分”是一个 ISeq 本身。此外,它被第一个 ISeq 缓存。因此,绑定到 Var 的 ISeq 的“剩余”ISeq 的第一个元素永远不会被垃圾收集,因为对它的引用被绑定到 Var 的 ISeq 的“剩余”ISeq 持有,而这个 ISeq 不会被 GC 处理,因为它被绑定到 Var 的 ISeq 缓存为“其余”组件,而只要它绑定到 Var,它就不会被 GC,而这反过来通常永远不会GC 是因为它是命名空间中的顶级 Var。

    很明显,如果 Var 不再被其命名空间 (ns-unmap) 持有或命名空间本身被丢弃 (remove-ns),它就会被 GC。如果它碰巧拥有一个 ISeq,那么当且仅当它没有被其他一些代码保留时,该 ISeq 才会被 GC 处理——当然,通常的 GC 规则适用。对于binding 引入的绑定和let 引入的本地绑定,以上所有内容都适用于绑定的模寿命问题。 (这不是这个问题的主题。)

    【讨论】:

    • 保持序列的 var 是什么?定义函数 fib-seq 是否会创建一个指向该函数的 var,并且当该函数返回一个序列时,该函数会保留对该序列的引用?是否有任何返回序列的函数会保留它?
    • 您问题中的示例根本没有定义任何功能!请注意,您使用的是def 而不是defn,您定义fib-seq 要引用的是一个序列,而不是一个函数。因此,在这种特殊情况下,在评估您的示例 def 表单后,符号 fib-seq 在当前命名空间中解析为的 Var 是由 lazy-cat 表单生成的斐波那契数列所保持的。这不会进入无限循环的事实是由于lazy-cat 产生了一个 lazy 序列,当您向它请求元素时就会实现。
    • 您在此 Q 中链接到的其他问题的答案包含 Christophe Grand 的函数,该函数以懒惰的方式生成所有斐波那契数字的序列,而不是抓住头部 - 请注意,在该函数中 从未为整个序列分配名称。如果您执行(def fib-seq (fibo)) 之类的操作,那么只要此绑定没有被另一个(def fib-seq ...) 表达式或alter-var-root 更改,那么您肯定会尽可能多地保留您已经意识到的序列。有关def / defn 的信息,另请参阅clojure.org/special_forms
    • 哦,还有一件事...如果您执行类似(doseq [fib (fibo)] (println fib)) 的操作,那么您将永远打印斐波那契数列,但最初调用fibo 产生的序列不会t 被保留,主要是因为它永远不会绑定到任何名称。这是否使它更清晰......?
    • 是的,您最初的回答需要一段时间才能理解,我才意识到您在说什么。感谢您的澄清。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2011-09-17
    相关资源
    最近更新 更多