clojure - 捕获序列的头部

标签 clojure

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

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

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

最佳答案

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

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

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

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

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

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

关于clojure - 捕获序列的头部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2214258/

相关文章:

clojure - 在调用转换时,集合值会传递多少个归约函数?

debugging - 返回 Clojure 中当前所有作用域符号的表达式?

recursion - 用于计算组合的尾递归 Clojure 函数

python - 是否有一个 python 函数可以返回一个添加了新键的新字典,比如 clojure 中的 assoc?

clojure - 如何在 Clojure 中链接函数调用?

sql - 当我尝试连接到 Postgresql 数据库时,Leiningen/Clojure 表现得很奇怪

clojure - 使用Clojure中的自定义方法漂亮地打印记录

string - Clojure:如何将 clojure 代码转换为可计算的字符串?它大部分有效,但列表被转换为原始括号,这失败了

java - 如何将 java.sql.Timestamp 转换为基于 UTC 的字符串?

python - Clojure 素数惰性序列