2015-11-03 46 views
4

據我所知,在動態變量上設置新綁定會影響綁定中調用的所有函數以及從這些函數調用的所有函數。動態變量的Clojure綁定無法按預期方式工作

爲什麼綁定在下面的第一個示例中似乎丟失? map返回其內部的結合定義,但外評價懶惰序列 -

(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") 

回答

5

這是由lazyness引起的。您需要從內部強制進行評估:

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

嗯,就是這樣。能夠在這裏找到更多有關綁定陷阱的更多信息:[鏈接](http://cemerick.com/2009/11/03/be-mindful-of-clojures-binding/) –

+0

@DavidVail,請注意,事情自撰寫博客文章以來已有所改進(僅在一個月後,Clojure 1.1引入了「bound-fn」)。 – galdre

2

確實,懶惰和動態綁定可能導致問題;然而,放棄懶惰並不是唯一的解決辦法。如果您想保留懶惰(或使用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) 
0

另一種解決方案是通過lazy-genyieldfrom 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}) 
相關問題