Обновлено, чтобы учесть комментарий Стюарта Сьерры (с упоминанием clojure.core/intern
).
Использование eval
здесь хорошо, но может быть интересно знать, что в этом нет необходимости, независимо от того, известно ли, что Vars уже существуют. На самом деле, если они известны, то я думаю, что решение alter-var-root
ниже будет чище; если бы они могли не существовать, то я бы не стал настаивать на том, чтобы мое альтернативное предложение было намного чище, но, похоже, оно предназначено для самого короткого кода (если мы игнорируем служебную информацию из трех строк для определения функции), поэтому я просто опубликую это на ваше рассмотрение.
Если известно, что Var существует:
(alter-var-root (resolve (symbol "foo")) (constantly new-value))
Так что вы могли бы сделать
(dorun
(map #(-> %1 symbol resolve (alter-var-root %2))
["x" "y" "z"]
[value-for-x value-for-y value-for z]))
(Если бы одно и то же значение использовалось для всех Vars, вы могли бы использовать (repeat value)
в качестве последнего аргумента для сопоставления или просто поместить его в анонимную функцию.)
Если Vars, возможно, потребуется создать, тогда вы можете написать функцию для этого (еще раз, я бы не стал утверждать, что это чище, чем eval
, но в любом случае - просто для интереса он):
(defn create-var
;; I used clojure.lang.Var/intern in the original answer,
;; but as Stuart Sierra has pointed out in a comment,
;; a Clojure built-in is available to accomplish the same
;; thing
([sym] (intern *ns* sym))
([sym val] (intern *ns* sym val)))
Обратите внимание, что если оказалось, что Var уже был интернирован с заданным именем в заданном пространстве имен, то это ничего не меняет в случае с одним аргументом или просто сбрасывает Var до указанного нового значения в случае с двумя аргументами. С этим вы можете решить исходную проблему следующим образом:
(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"]))
Некоторые дополнительные примеры:
user> (create-var 'bar (fn [_] :bar))
#'user/bar
user> (bar :foo)
:bar
user> (create-var 'baz)
#'user/baz
user> baz
; Evaluation aborted. ; java.lang.IllegalStateException:
; Var user/baz is unbound.
; It does exist, though!
;; if you really wanted to do things like this, you'd
;; actually use the clojure.contrib.with-ns/with-ns macro
user> (binding [*ns* (the-ns 'quux)]
(create-var 'foobar 5))
#'quux/foobar
user> quux/foobar
5