От това, което разбирам, задаването на ново свързване на динамичен варим засяга всички функции, наречени в рамките на това обвързване, и всички функции, наречени от тези функции.
Защо връзката изглежда да бъде загубена в първия пример по-долу?
(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 за отговор № 1Това се дължи на мързеливостта - map
връща мързеливата последователност, която се дефинира вътре в връзката, но се оценява извън нея. Трябва да наложите оценката отвътре:
(binding [*out-dir* "/home/dave"]
(doall (map #(str *out-dir* %) [1 2 3])))
2 за отговор № 2
Вярно е, че мързеливостта и динамичните свързвания могат да причинят проблеми, но изоставянето на мързел не е единственото решение. Ако желаете да запазите мързел (или да използвате динамични свързвания с pmap
), използвайте bound-fn
или 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)
0 за отговор № 3
Друго решение е да използвате функциите на генератора Python стил, достъпни чрез lazy-gen
и yield
от библиотеката Tupelo:
(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})