2011-12-10 70 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