8

我刚开始使用宏并做了一个简单的宏来从列表中返回最大数量。

(defmacro macro-max [list] (apply max list))

如果我做一个函数来做同样的事情,它会的。

(defn  macro-max [list] (apply max list))

我只是在探索 Clojure,所以我了解的不多。

对于专家来说,我可能听起来很傻,但看起来我几乎可以定义一个函数而不是宏。

4

4 回答 4

8

当你不想评估你的论点除非需要时,差异就会显现出来。

考虑这个例子:

(defn unless [pred body]
  (when (not pred)
    body))

这不起作用,因为函数的参数被急切地评估。所以body总是运行,如下图:

(unless (zero? 2)
        (prn "Not zero!"))
;; "Not zero!"

(unless (zero? 0)
        (prn "Not zero!"))
;; "Not zero!"

上面的两次执行都打印“非零!”,这显然是错误的。

编写我们的实用程序的唯一方法unless是使用宏:

(defmacro unless [pred body]
  `(when (not ~pred)
     ~@body))

现在,如果我们试一试,我们会看到它按预期工作:

(unless (zero? 2)
        (prn "Not zero!"))
;; "Not zero!"

(unless (zero? 0)
        (prn "Not zero!"))
;; this prints nothing

宏仅在开发人员决定时评估其参数。

您会注意到宏中的一些特殊字符,例如 `、~ 和 ~@。它们分别代表 syntax-quote、unquote 和 unquote-splicing,并在此处进行解释。

我建议你研究这些符号。

希望这可以帮助。

于 2013-04-15T07:17:25.757 回答
1

如果一个函数可以做,那么一定要使用一个函数。Clojure 库遵循此规则,因此可能在那里定义的大多数宏不能真正表示为函数。拿->>宏:

(->> 
   "abcdefghij"
   (take 5)
   (drop 3)
   (apply str))

以上评估为“de”。函数无法做到这一点,因为宏的参数在自己评估时没有意义 - 评估(take 5)时会引发错误。

宏所做的是在计算表单之前修改表单(通过插入前一个表达式作为表单的最后一个参数)。这是只有宏可以做的事情,而不是函数。

于 2013-04-15T07:19:58.487 回答
1

宏是函数。区别仅在于评估时间(宏在宏扩展时评估,这是编译中的一个步骤,函数在运行时评估)。(mac a b)此外,宏适用于 S-Expressions(所以宏有点像一个函数,你引用每个参数,即你可以很容易地用一个函数替换任何宏(fun 'a 'b)

于 2013-04-15T12:16:19.813 回答
-5

我没有使用宏的经验,但它可以让你通过你的函数来扩展编译器。我不确定,但它是在运行时评估的。

于 2013-04-15T07:18:52.170 回答