给定变量的名称列表,我想将这些变量设置为表达式。
我试过了:
(doall (for [x ["a" "b" "c"]] (def (symbol x) 666)))
...但这会产生错误
java.lang.Exception:def的第一个参数必须是符号
有人能告诉我正确的方法吗?
答案 0 :(得分:34)
Clojure的“实习生”功能就是为了这个目的:
(doseq [x ["a" "b" "c"]]
(intern *ns* (symbol x) 666))
答案 1 :(得分:13)
(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666))))
回应你的评论:
此处不涉及任何宏。 eval
是一个函数,它接受一个列表并将执行该列表的结果作为代码返回。 `和〜是创建部分引用列表的快捷方式。
`表示除非前面带有〜
,否则应引用以下列表的内容〜以下列表是一个函数调用,应该执行,而不是引用。
所以``(def~(符号x)666)is the list containing the symbol
def , followed by the result of executing
符号x followed by the number of the beast. I could as well have written
(eval(list'snd(符号x)666))`来实现同样的效果。
答案 2 :(得分:7)
更新了Stuart Sierra的评论(提及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
答案 3 :(得分:3)
正常函数调用的评估规则是评估列表中的所有项目,并将列表中的第一项作为函数调用,列表中的其余项目作为参数。
但是您无法对特殊表单或宏的评估规则做出任何假设。宏调用生成的特殊表单或代码可以评估所有参数,或者从不评估它们,或者多次评估它们,或者评估一些参数而不评估其他参数。 def
是一种特殊形式,它不会评估它的第一个参数。如果确实如此,它就行不通。评估foo
中的(def foo 123)
会在大多数情况下导致“没有这样的'foo'”错误(如果已经定义了foo
,您可能不会自己定义它)。
我不确定你使用的是什么,但它似乎不是很惯用。在你的程序的任何地方使用def
通常意味着你做错了。
(注意:doall
+ for
= doseq
。)