动态var的Clojure绑定不能按预期工作

时间:2015-11-03 13:23:57

标签: clojure

据我所知,在动态var上设置新绑定会影响该绑定中调用的所有函数,以及从这些函数调用的所有函数。

为什么绑定在下面的第一个例子中似乎丢失了?

(def ^:dynamic *out-dir* "/home/user")

(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives:    ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")

(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")

3 个答案:

答案 0 :(得分:5)

这是由懒惰引起的 - map返回一个懒惰序列,该序列在绑定中定义但在外部进行评估。您需要从内部强制进行评估:

(binding [*out-dir* "/home/dave"] 
  (doall (map #(str *out-dir* %) [1 2 3])))

答案 1 :(得分:3)

懒惰和动态绑定确实会导致问题;然而,放弃懒惰不是唯一的解决方案。如果您希望保留懒惰(或使用pmap动态绑定),请使用bound-fnbound-fn*

(def ^:dynamic x 0)

=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)

=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)

=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)

答案 2 :(得分:0)

另一种解决方案是使用lazy-genyield from the Tupelo library提供的Python样式生成器函数:

(ns tst.demo.core
  (:use demo.core tupelo.test)
  (:require
    [tupelo.core :as t] ))
(t/refer-tupelo)

(def ^:dynamic foo 1)

(dotest
  (let [result (binding [foo 3]
                 (lazy-gen
                   (doseq [x (range 3)]
                     (yield {:foo foo :x x})))) ]
    (println result)))

result => ({:foo 3, :x 0} 
           {:foo 3, :x 1}
           {:foo 3, :x 2})