2011-12-10 166 views
1

我想創建一個函數(thunk),它將返回列表中的連續元素。做這個的最好方式是什麼?我寫了一個基於如何局部變量的Clojure中工作的一個明顯缺陷瞭解此代碼:Clojure局部變量

(defn reader-for [commands] 
     (with-local-vars 
     [stream commands] 
     (fn [] 
      (let 
      [r (var-get stream)] 
      (if (empty? r) 
       nil 
       (let 
       [cur (first r) 
       _ (var-set stream (rest r))] 
       cur)))))) 

在這段代碼中,我得到:

#<CompilerException java.lang.IllegalStateException: Var null/null is unbound. (Chapel.clj:1)> 

這似乎表明,與本地 - 瓦爾是動態作用域。真的嗎?有沒有詞彙範圍的選擇?謝謝你的幫助。

+3

你的意思是連續的? '[1 2 3 4] => [1 2] [3 4]'或'[1 2 3 4] => [1 2] [2 3] [3 4]'?給我們一個輸入和期望輸出的例子 –

+0

你想在經歷它們的最後分組所有的元素? – octopusgrabbus

回答

4

如果需要可變狀態,使用Clojure的引用類型之一:

 
user=> (defn reader-for [coll] 
     (let [a (atom coll)] 
      (fn [] 
      (let [x (first @a)] 
       (swap! a next) 
       x)))) 
#'user/reader-for 
user=> (def f (reader-for [1 2 3])) 
#'user/f 
user=> (f) 
1 
user=> (f) 
2 
user=> (f) 
3 
user=> (f) 
nil 

此外,let是詞法範圍,binding是動態作用域。

編輯:艾倫指出的線程安全版本。

 
(defn reader-for [coll] 
    (let [r (ref coll)] 
    #(dosync 
     (let [x (first @r)] 
     (alter r next) 
     x)))) 

而只是爲了好玩,與原子的線程安全版本(不這樣做):

 
(defn reader-for [coll] 
    (let [a (atom coll)] 
    (fn [] 
     (let [ret (atom nil)] 
     (swap! a (fn [[x & xs]] 
        (compare-and-set! ret nil x) 
        xs)) 
     @ret)))) 
+3

該解決方案很好,但不是線程安全的。在這裏ref是更好的,當然,有些方法可以讓一個原子變成你想做的事情。 – amalloy

+1

非常正確。一個原子似乎適合,因爲只有一個「東西」,但由於讀取和修改必須分開執行,所以仍然需要協調。 –

+3

實際上你只需要一個原子,如果你在原子的不同槽中同時保留返回值和「狀態」:'(let [a(atom [nil coll])](fn [](first(swap!a (fn [[ret coll]]((juxt first rest)coll))))))' – amalloy