如何将列表表单转换为clojure中的函数

hai*_*jin 4 clojure

我有一个像这样定义的函数规范,我想把它评估成一个函数对象,所以我可以传递.

(def spec '(foo [n] (* 2 n)))
Run Code Online (Sandbox Code Playgroud)

我可以像这样创建一个宏

(defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body))
Run Code Online (Sandbox Code Playgroud)

然后以下调用将给我函数foo.当用3调用时,(foo 3)将返回6.

(evspec foo n (* 2 n))
Run Code Online (Sandbox Code Playgroud)

但是,如果我从上面定义的规范中获取函数体,则返回的函数foo不会评估体形(*2 n),而是返回体形.

(let [foo (first spec) arg (first (second spec)) body (last spec)]
  (evspec foo arg body))

user=> (foo 3)
(* 2 n)
Run Code Online (Sandbox Code Playgroud)

我注意到现在创建的foo函数是$ eval $ foo

user=> foo
#<user$eval766$foo__767 user$eval766$foo__767@39263b07>
Run Code Online (Sandbox Code Playgroud)

而工作foo功能是

user=> foo
#<user$foo user$foo@66cf7fda>
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么会有差异,我怎样才能使它发挥作用?我想在没有回复eval的情况下有办法吗?来自javascript背景,不知怎的,我总是认为eval是邪恶的.

Mic*_*zyk 5

没有,一般不可能做到这一点eval.宏只是一个函数,它在编译时从字面上传递它的参数表达式(通常根本不可能知道它们的值在运行时可能是什么).特别是,在对问题文本中evspeclet表单内部的调用中,返回值为(* 2 n),evspec宏扩展器在字面上看到符号foo和符号n作为其位置参数和(body)(包含单个符号的seq body)作为其"rest"参数; 返回值与这些输入一致.

但是,eval用于这种目的是非常好的.重要的是要记住它具有相当大的运行时成本,所以你需要稍微使用它,但是一旦你使用了函数eval,它就是一个非常好的Clojure函数,就像其他任何函数一样快.

另外,请注意,虽然在JavaScript中eval对文本进行操作,但Clojure eval在Clojure数据结构上运行 - 实际上相同的数据结构宏运行 - 这可以说它不易出错.