我刚开始使用宏并做了一个简单的宏来从列表中返回最大数量。
(defmacro macro-max [list] (apply max list))
如果我做一个函数来做同样的事情,它会的。
(defn macro-max [list] (apply max list))
我只是在探索 Clojure,所以我了解的不多。
对于专家来说,我可能听起来很傻,但看起来我几乎可以定义一个函数而不是宏。
我刚开始使用宏并做了一个简单的宏来从列表中返回最大数量。
(defmacro macro-max [list] (apply max list))
如果我做一个函数来做同样的事情,它会的。
(defn macro-max [list] (apply max list))
我只是在探索 Clojure,所以我了解的不多。
对于专家来说,我可能听起来很傻,但看起来我几乎可以定义一个函数而不是宏。
当你不想评估你的论点除非需要时,差异就会显现出来。
考虑这个例子:
(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,并在此处进行解释。
我建议你研究这些符号。
希望这可以帮助。
如果一个函数可以做,那么一定要使用一个函数。Clojure 库遵循此规则,因此可能在那里定义的大多数宏不能真正表示为函数。拿->>
宏:
(->>
"abcdefghij"
(take 5)
(drop 3)
(apply str))
以上评估为“de”。函数无法做到这一点,因为宏的参数在自己评估时没有意义 - 评估(take 5)
时会引发错误。
宏所做的是在计算表单之前修改表单(通过插入前一个表达式作为表单的最后一个参数)。这是只有宏可以做的事情,而不是函数。
宏是函数。区别仅在于评估时间(宏在宏扩展时评估,这是编译中的一个步骤,函数在运行时评估)。(mac a b)
此外,宏适用于 S-Expressions(所以宏有点像一个函数,你引用每个参数,即你可以很容易地用一个函数替换任何宏(fun 'a 'b)
。
我没有使用宏的经验,但它可以让你通过你的函数来扩展编译器。我不确定,但它是在运行时评估的。