可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
From what I understand, setting a new binding on a dynamic var affects all functions called within that binding, and all functions called from those functions.
Why does the binding appear to be lost in the first example below?
(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")
回答1:
This is caused by lazyness - map
returns a lazy sequence which is defined inside the binding but is evaluated outside. You need to force the evaluation from inside:
(binding [*out-dir* "/home/dave"] (doall (map #(str *out-dir* %) [1 2 3])))
回答2:
It's true that laziness and dynamic bindings can cause problems; however, abandoning laziness is not the only solution. If you wish to preserve laziness (or to use dynamic bindings with pmap
), use bound-fn
or bound-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)
回答3:
Another solution is to use Python-style generator functions available via lazy-gen
and yield
from the Tupelo library:
(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})