/ / Clojure свързване на динамично var не работи, както се очаква - clojure

Clojure свързване на динамични var не работи, както се очаква - clojure

От това, което разбирам, задаването на ново свързване на динамичен варим засяга всички функции, наречени в рамките на това обвързване, и всички функции, наречени от тези функции.

Защо връзката изглежда да бъде загубена в първия пример по-долу?

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