clojure宏中符号求值的正确方法

clojure宏中符号求值的正确方法,clojure,macros,Clojure,Macros,我有一个文件,其中包含一些可信的clojure源代码: ((+ab)(*ab)(-ab)) 对于列表中的每个项目,我要生成一个匿名函数: (fn[ab](+ab)) (fn[ab](*ab)) (fn[ab](-ab)) 如果我打电话给下面的马可 (定义宏创建fn [args exprs] `(fn~args~exprs)) 直接使用一些clojure代码,它可以完美地工作: user=>(宏扩展-1'(创建fn[ab](*ab))) (clojure.core/fn[ab](*ab))

我有一个文件,其中包含一些可信的clojure源代码:

((+ab)(*ab)(-ab))
对于列表中的每个项目,我要生成一个匿名函数:

(fn[ab](+ab))
(fn[ab](*ab))
(fn[ab](-ab))
如果我打电话给下面的马可

(定义宏创建fn
[args exprs]
`(fn~args~exprs))
直接使用一些clojure代码,它可以完美地工作:

user=>(宏扩展-1'(创建fn[ab](*ab)))
(clojure.core/fn[ab](*ab))
但是,当我将文件的上下文绑定到本地并尝试映射宏时,它将不起作用。在访问第一个生成的函数时,我收到错误消息“java.lang.RuntimeException:无法解析此上下文中的符号:a”

(请注意,我必须在宏中添加一个额外的
eval
,以获取map使用的匿名函数中使用的符号
e
的值)

(定义宏创建fn
[args exprs]
`(让[e#(eval~exprs)]
(fn~args)
e#))
(让[exprs(读取字符串)(+ab)(*ab)(-ab)))
fns(地图
(fn[e](创建fn[a b]e))
exprs)]
(第一次)

非常感谢您的帮助

让我们看看宏扩展后的整个代码。此代码:

(let [exprs (read-string "((+ a b) (* a b) (- a b))")
      fns   (map
             (fn [e] (create-fn [a b] e))
             exprs)]
  (first fns))
扩展到此,其中
e_uu900_u_u自动_uu
是由
e#
生成的符号:

为什么这样不行?一个原因是
a
b
甚至不在
(eval e)
的范围之内。下一步您可能会尝试以下方法:

(defmacro create-fn [args exprs] `(fn ~args (eval ~exprs)))
展开后,生成的函数如下所示:

(let [exprs (read-string "((+ a b) (* a b) (- a b))")
      fns   (map
             (fn [e] (fn [a b] (eval e)))
             exprs)]
  (first fns))
(map
 (fn [e] (eval (concat '(fn [a b]) (list e))))
 exprs)
这看起来不错,但因为
eval
在一个空的词汇环境中求值。换句话说,
eval
即使使用此代码也不会看到
a
b

您可以放弃宏,只需手动将代码转换为可以评估的内容,如下所示:

(let [exprs (read-string "((+ a b) (* a b) (- a b))")
      fns   (map
             (fn [e] (fn [a b] (eval e)))
             exprs)]
  (first fns))
(map
 (fn [e] (eval (concat '(fn [a b]) (list e))))
 exprs)
或者,您可以将变量
a
b
声明为动态变量,然后在计算表达式之前使用
binding
进行设置

(declare ^:dynamic a ^:dynamic b)

(let [exprs (read-string "((+ a b) (* a b) (- a b))")
      fns   (map
             (fn [e] (fn [a1 b1] (binding [a a1 b b1] (eval e))))
             exprs)]
  (first fns))
如果您不想在名称空间中包含
a
b
,则可以设置另一个名称空间和



我建议的解决方案不使用宏。它们在这里没有用处,因为宏是在编译时展开的,而表达式是在运行时读取的。如果你真的想在这里使用宏,你需要移动
读取字符串
,文件处理代码在
defmacro

exprs
绑定到
(+ab)
,你调用
eval
,如果我在宏
create fn
中放入
(println exprs)
,错误会被纠正,它会打印“e”。我的理解是,在宏扩展期间,
exprs
绑定到符号
e
,而不是它的值。因此,我想我需要在
exprs
上调用
eval
,以获得符号
e
的值……非常感谢您的详细回答!这有助于我在宏观评估期间理解词汇范围。令人伤心的是,我将使用非宏解决方案,因为它看起来更容易:-)