在宏中使用列表和返回刻度之间的区别

时间:2012-02-23 17:24:13

标签: clojure

在概念层面,LISP(和方言)中的宏取一段代码(作为列表)并返回另一段代码(再次作为列表)。

基于以上原则,一个简单的宏可以是:

(defmacro zz [a] (list print a))
;macroexpand says : (#<core$print clojure.core$print@749436> "Hello")

但在clojure中,这也可以写成:

(defmacro zz [a] `(print ~a))
;macroexpand says : (clojure.core/print "Hello")

我不确定这里的区别,哪些应该是首选方式。第一个看起来很简单,因为我们应该返回列表并避免使用像back tick这样的奇怪字符。

4 个答案:

答案 0 :(得分:8)

还没有人指出这一点......你的2个宏之间的区别是:你的第二个形式(使用反引号)

(defmacro zz [a] `(print ~a))

相当于:

(defmacro zz [a] (list 'print a))

这与您的第一个例子不同:

(defmacro zz [a] (list print a))

请注意缺少的单引号 - 这就是您的宏扩展不同的原因。我同意其他人的帖子:如果你的宏有一个相当简单的“形状”,使用反引用更常规。如果你必须进行代码遍历或动态构造(即复杂的宏),那么使用列表并构建它往往是完成的。

我希望这种解释是有道理的。

答案 1 :(得分:6)

在某种程度上,明确构建列表是“最简单的”,因为您需要知道的核心概念很少:只需接受一个列表并更改它,直到您有一个新列表。 Backtick是“模板化”代码块的便捷快捷方式;可以在没有它的情况下编写任何宏,但对于任何大宏,它很快就会变得非常不愉快。例如,考虑将let编写为fn上的宏的两种方式:

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    `((fn [~@names]
        (do ~@body))
      ~@vals)))

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    (cons (list `fn (vec names) (cons `do body))
          vals)))

在第一种情况下,使用反引号可以清楚地表明你正在编写包含正文的名称的函数,然后用值调用它 - 宏代码与扩展代码“形状”相同,所以你可以想象它会是什么样子。

在第二种情况下,只有conslist到处都是,弄清楚扩展会是什么样子真的很头疼。当然,情况并非总是如此:有时候在没有反复的情况下写东西会更清楚。

Kyle Burton提出了另一个非常重要的观点:print'print不同!您的宏扩展应包含符号 print,而不是其值(这是一个函数)。在代码中嵌入对象(例如函数)非常脆弱,只能偶然使用。因此,请确保您的宏扩展为您自己实际编写的代码,并让评估系统执行繁重的工作 - 您可以键入符号print,但无法键入指向当前值的指针函数print

答案 2 :(得分:5)

它们之间存在风格差异。您的示例非常简单,但在更复杂的宏中,差异会更大。

例如,“Clojure的喜悦”一书中定义的除非宏:

(defmacro unless [condition & body]
    `(if (not ~condition)
        (do ~@body)))

从书中可以看出:

  

Syntax-quote允许以下if-form充当表达式的一种模板      任何宏的使用都会在扩展时使用。

创建宏时,请始终选择最具可读性和惯用的风格。

相比之下,上面的代码可以等效地编写:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))

答案 3 :(得分:1)

根据我的经验,他们是等同的。虽然可能有一些我不知道的边缘情况。

@islon的例子可以等同地写成:

相比之下,上面的代码可以等效地编写:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))