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

时间:2013-07-10 00:39:19

标签: clojure

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

(def spec '(foo [n] (* 2 n)))

我可以像这样创建一个宏

(defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body))

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

(evspec foo n (* 2 n))

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

(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>

而工作foo函数是

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

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

1 个答案:

答案 0 :(得分:5)

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

然而,使用body来达到这种目的是完全没问题的。重要的是要记住它具有相当大的运行时成本,所以你需要稍微使用它,但是一旦你使用eval生成一个函数,它就是一个非常好的Clojure函数,就像它一样快任何其他。

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