在Clojure中,如何定义一个由字符串命名的变量?

Car*_*icz 18 symbols function clojure

给定变量的名称列表,我想将这些变量设置为表达式.

我试过这个:

(doall (for [x ["a" "b" "c"]] (def (symbol x) 666)))
Run Code Online (Sandbox Code Playgroud)

......但这会产生错误

java.lang.Exception:def的第一个参数必须是Symbol

有谁能告诉我正确的方法,请?

Stu*_*rra 34

Clojure的"实习生"功能就是为了这个目的:

(doseq [x ["a" "b" "c"]]
  (intern *ns* (symbol x) 666))
Run Code Online (Sandbox Code Playgroud)


sep*_*p2k 13

(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666))))
Run Code Online (Sandbox Code Playgroud)

回应你的评论:

这里没有涉及宏.eval是一个获取列表并将执行该列表的结果作为代码返回的函数.`和〜是创建部分引用列表的快捷方式.

`表示除非前面带有〜,否则应引用以下列表的内容

〜以下列表是一个函数调用,应该执行,而不是引用.

所以``(def~(符号x)666)is the list containing the symboldef , 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))`来实现相同的效果.

  • @CarlSmotricz:我刚刚在代码中添加了一些解释.希望你现在能更充分地理解. (2认同)
  • 请!不要使用`eval`!使用`实习生'!但要注意重新定义现有的Vars! (2认同)

Mic*_*zyk 7

更新以考虑Stuart Sierra的评论(提及clojure.core/intern).

eval在这里使用很好,但是可能有趣的是,无论已知Vars是否存在,都没有必要.事实上,如果他们知道存在,那么我认为alter-var-root下面的解决方案更清洁; 如果它们可能不存在,那么我不会坚持我的替代命题更清晰,但似乎是为了最短的代码(如果我们忽略函数定义的三行开销),所以我只是发布它供你考虑.


如果已知Var存在:

(alter-var-root (resolve (symbol "foo")) (constantly new-value))
Run Code Online (Sandbox Code Playgroud)

所以你可以做到

(dorun
  (map #(-> %1 symbol resolve (alter-var-root %2))
       ["x" "y" "z"]
       [value-for-x value-for-y value-for z]))
Run Code Online (Sandbox Code Playgroud)

(如果所有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)))
Run Code Online (Sandbox Code Playgroud)

请注意,如果Var已经在给定的命名空间中使用给定的名称进行了实例化,那么这在单个参数的情况下不会改变任何内容,或者只是在两个参数的情况下将Var重置为给定的新值.有了这个,您可以像这样解决原始问题:

(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"]))
Run Code Online (Sandbox Code Playgroud)

一些额外的例子:

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
Run Code Online (Sandbox Code Playgroud)

  • Var.intern可作为内置的Clojure函数"实习生"使用 (2认同)