2010-02-06 23 views
9

的頭部讀我所確定的功能的最近的問題正在討論握住序列

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

爲持有到一個序列的頭部,但它發生,我重新閱讀我的回答,我已經掩蓋在細節方面他們很明顯,所以我回頭澄清並簡要說明。我知道fib-seq是一個var,只要它在它的周圍,它將包含序列中的所有元素,但我並不清楚序列如何保持的確切機制。任何澄清,將不勝感激。

回答

11

基本上,常規的GC規則適用...一個序列只是一個對象,並持有它的頭意味着堅持對這個對象的引用。這需要保持已經在內存中實現的序列的多少,因爲Clojure序列被緩存。

(更詳細的解釋如下: - 見粗體的片段爲它的要點... ;-))

Clojure中「A」序列是實現ISEQ接口的對象。這提供了提取序列的第一個元素和序列的其餘部分(另一個實現ISeq的對象)的方法。作爲一個關鍵細節,它們不僅要計算正確的對象(第一個/剩下的序列)並將其返回給調用者,還要將計算值緩存在內存中,以便後續請求更快 - 以及更多重要的是,即使ISeq是在一個可變的Java對象之上生成的,而這個Java對象在某個時刻發生了變化,那麼對於序列中相同元素的所有請求都會保證返回相同的值。 (注意,這對於Clojure序列的不可變語義是絕對至關重要的)。另一方面,Var是一個容器,它粗略地保存了一個指向某個Java對象的「指針」。如果這恰好是一個ISeq,那麼只要Var本身不是垃圾收集(如果它是當前存在的命名空間中的頂級var,它顯然是永遠不會的)或反彈,ISeq本身將不會被垃圾收集,特別是它用於緩存第一個/剩下的序列的內存將不會被釋放

至於序列的其他元素:綁定到Var的ISeq的'rest'是ISeq本身。另外,它被第一個ISeq緩存。因此,綁定到Var的ISeq的'rest'ISeq的第一個元素永遠不會被垃圾收集,因爲對它的引用被綁定到Var的ISeq的'rest'ISeq持有,並且此ISeq不會被GC'd,因爲它被綁定到Var的ISeq被緩存爲'rest'組件,只要它被綁定到Var,反過來將不會GC'd,而Var通常不會被GC'd,因爲它是名稱空間中的頂級Var。

顯然瓦爾如果它不再由它的命名空間(ns-unmap)或本身已被扔在命名空間(remove-ns)舉行到GC'd。如果它碰巧持有一個ISeq,那麼當且僅當它不被其他一些代碼持有時,該ISeq將被GC'd - 通常的GC規則當然適用。對於使用binding引入的綁定以及使用let引入的本地綁定,以上所有內容都應用了綁定的模生存期問題。 (這不是這個問題的主題)

+0

保持序列的變量是什麼?定義函數fib-seq是否創建了一個指向該函數的var,並且當該函數返回一個序列時,函數會保持對該序列的引用?任何函數都會返回一個序列來保存它嗎? – 2010-02-06 21:34:40

+0

您的問題中的示例根本沒有定義任何功能!請注意,您正在使用'def'而不是'defn',並且您定義的'fib-seq'指的是一個序列,而不是一個函數。因此,在這種特殊情況下,在您的示例「def」形式被評估後,符號「fib-seq」在當前名稱空間中解析到的Var是由「lazy-cat」形式生成的斐波那契數列序列。事實上,這並沒有進入無限循環,這是因爲'lazy-cat'產生了一個* lazy *序列,當你從中請求元素時就會被實現。 – 2010-02-06 22:28:23

+2

你在這個Q中鏈接到的另一個問題的答案包含Christophe Grand的函數,它以懶惰的方式產生所有斐波那契數列的序列,並且不需要保持頭部 - 請注意,在該函數中*沒有名稱被分配給整個序列*。如果你做了類似'(def fib-seq(fibo))'的話,那麼你肯定會保持儘可能多的順序,只要這個綁定沒有被另一個'( def fib-seq ...)'表達式或'alter-var-root'。有關'def' /'defn'的信息,請參閱http://clojure.org/special_forms。 – 2010-02-06 22:35:45