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

如何在clojure中将列表表单转换为函数,clojure,Clojure,我有一个这样定义的函数规范,我想把它计算成一个函数对象,这样我就可以传递 (def spec '(foo [n] (* 2 n))) 我可以创建这样的宏 (defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body)) 然后下面的调用将给我函数foo。用3调用时,(foo3)将返回6 (evspec foo n (* 2 n)) 但是,如果我从上面定义的规范中获取函数体,那么foo返回的函数将不会计算body表单(*2n

我有一个这样定义的函数规范,我想把它计算成一个函数对象,这样我就可以传递

(def spec '(foo [n] (* 2 n)))
我可以创建这样的宏

(defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body))
然后下面的调用将给我函数foo。用3调用时,(foo3)将返回6

(evspec foo n (* 2 n))
但是,如果我从上面定义的规范中获取函数体,那么foo返回的函数将不会计算body表单(*2n),而是返回body表单

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

user=> (foo 3)
(* 2 n)
我注意到现在创建的foo函数是$eval$foo

user=> foo
#<user$eval766$foo__767 user$eval766$foo__767@39263b07>
user=>foo
#
而工作的foo函数是

user=> foo
#<user$foo user$foo@66cf7fda>
user=>foo
#

有人能解释为什么会有不同,我怎样才能让它起作用?我想有一个不回答评估的方法?来自javascript背景,不知何故,我总是认为eval是邪恶的。

没有
eval
一般来说是不可能做到的。宏只是一个函数,它在编译时按字面意思传递参数表达式(通常根本不可能知道它们在运行时的值)。特别是,在问题文本中的
let
表单中对
evspec
的调用中,返回值为
(*2n)
evspec
宏扩展器将符号
foo
和符号
n
视为其位置参数和
(body)
(一个包含单个符号
正文
)的seq,作为其“rest”参数;返回值与这些输入一致

但是,将
eval
用于这种用途是非常好的。请记住,它具有相当大的运行时成本,因此您可能希望稍微节约使用,但一旦您使用
eval
生成函数,它将是一个非常好的Clojure函数,速度与其他函数一样快


另外,请注意,在JavaScript
eval
中,当对文本进行操作时,Clojure的
eval
对Clojure数据结构进行操作——事实上,宏操作的数据结构是相同的——这可以说使它不那么容易出错。

如果要传递函数对象,只需使用(defn foo[…)并在数据结构上传递符号foo.+1表示clojure eval,而在文本上传递符号js eval!