2015-11-03 42 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}) 
相关问题