Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure宏:解析变量_Clojure_Macros - Fatal编程技术网

Clojure宏:解析变量

Clojure宏:解析变量,clojure,macros,Clojure,Macros,在Clojure中,我似乎对宏了如指掌。我可能缺少一些基本的东西。首先,请允许我描述一个我想要的例子 (defmacro macrotest [v] `(d ...)) (def a '(b c)) (macroexpand-1 '(macrotest a)) ; => (d (b c)) 换句话说,传递给macrotest的var被解析,但没有进一步评估 为宏提供var的值: (defmacro macrotest [v] `(d ~v)) (macroexpand-1 '(macro

在Clojure中,我似乎对宏了如指掌。我可能缺少一些基本的东西。首先,请允许我描述一个我想要的例子

(defmacro macrotest [v] `(d ...))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d (b c))
换句话说,传递给
macrotest
var
被解析,但没有进一步评估

为宏提供var的值:

(defmacro macrotest [v] `(d ~v))
(macroexpand-1 '(macrotest (b c)))
; => (d (b c))
但提供var不会:

 (def a '(b c))
 (macroexpand-1 '(macrotest a))
 ; => (d a)
是否可以在Clojure宏中解析
var
,但不计算其值

编辑:我想要的似乎可以通过
eval

  (defmacro macrotest [v] `(d ~(eval v)))
  (def a '(b c))
  (macroexpand-1 '(macrotest a))
  ; => (user/d (b c))

重要的是要理解宏是在编译时计算的,而不是在运行时

在编译时,var a还没有值,因此它的值不会传递给宏,只传递给它的名称。然而,当您显式地传递“值”时,那么它在编译时就存在了,并且您可以看到它“工作”

词汇环境 在宏中使用
eval
是不好的做法。当然,以下方法很有效:

(macroexpand-1 '(macrotest a))
; => (user/d (b c))
。。。但是,这并不像预期的那样有效:

(macroexpand-1 '(let [a "oh no"]
                  (macrotest a)))
; => (user/d (b c))
当然,您希望宏按照本地定义的方式计算
a
,而不是使用绑定到它的全局变量,但是您不能,因为宏在正确的词汇上下文中不计算
v
。这是理解宏的关键:宏接受代码并生成代码;由该代码操纵的任何数据尚未提供给您。换句话说,宏展开的时刻可能与其结果代码的求值时间完全无关:在宏执行时求值的任何内容都可能过早求值,即数据的相关性

你想做什么? 有一个名为
macrotest
的表单,它应该接受一个参数
v
,然后像应用了表单
d
一样执行。但是:

  • (宏测试(bc))
    不应评估
    (bc)
    ,只需将其原封不动地复制以生成
    (d(bc))
  • (宏测试a)
    应该评估
    a
    ,它将生成一个引用的表单,并将该表单放入生成的代码中,同时返回
    (d(b c))
这里有个问题,因为在一种或另一种情况下,意思会发生根本性的变化。要么计算参数,在这种情况下,必须引用
(b c)
,然后编写:

(defmacro macrotest [v] `(d ~v))
。。。这要求
d
准备好处理引用的参数;或者你同意不评估这个论点。 使用与上述相同的
d
,可以显式引用参数:

(defmacro macrotest [v] `(d '~v))

但是,如果
d
本身是一个不计算其参数的宏,则必须避免使用
~v
前面的引号

谢谢。我知道宏是在编译时计算的,但我可能不理解这一点的所有含义。所以我最后一个问题的答案是“否”?没有办法将运行时值传递到宏中?我认为没有办法,除了
eval
之类的方法。在lisps中,宏通常用于构建新的“语法”,因此,如果您发现在编译时确实必须具有运行时值,也许您应该重新考虑您试图实现的目标;)如果宏扩展为函数,是否可能?使用
eval
似乎适用于我的用例(参见编辑的文章)。我不确定这是否是个好主意。回答得很好!我确实忘记了宏中使用的变量的范围。不过,我对你在答覆的第二部分所引述的话有所怀疑。如果我使用
(defmacro macrotest[v]
(d'~v)`和
(defa'(bc))
,那么
(macrotest a)
扩展到
(d(quote a))
,而不是
(d(bc))
@jindrichm谢谢,关于最后一部分你是对的,我可能不是很清楚。这里的意图是,在第二种情况下,
macrotest
根本不评估其参数,只对不带引号的文本数据起作用;然后,引用该数据并将其传递给
d
。因此,将
a
赋予它将不会对表达式求值(与大多数宏一样)。