我是学习和使用clojure的新手,所以我对clojure中的宏有一个基本的问题。我没有找到你真正需要宏的情况,所以我想知道是否有一个真正的情况,只有一个宏,没有正常的功能或多方法解决你的问题。
有人可以为此展示一个简单的例子吗? 我想我不理解clojure中宏的概念。
答案 0 :(得分:2)
重要的是宏不会评估其参数,可以用于源代码的任意转换。
(defmacro when
"Evaluates test. If logical true, evaluates body in an implicit do."
[test & body]
`(if ~test (do ~@body)))
(defmacro when-not
"Evaluates test. If logical false, evaluates body in an implicit do."
[test & body]
`(if test nil (do ~@body)))
函数在这里不起作用,因为它必须在执行之前评估它的所有参数。
P.S。如果您对该主题感兴趣并希望了解更多信息,请参阅this my answer。这是关于Common Lisp,但它也可能对你有用。我还在答案的最后给出了一篇很酷的Paul Graham文章的链接。
P.S.S如果你想要一个新的有用宏的例子,我想在这里评论Paul Graham:
如果我能举一个强大的宏的例子,那就方便了!那个怎么样?但如果我这样做,那对于不了解Lisp的人来说就像是胡言乱语;这里没有空间来解释你需要知道的一切,以了解它的含义。
答案 1 :(得分:2)
Clojure宏采用文字代码,而函数采用评估代码。反过来,当您需要操作文字代码时,宏很有用。除了两个(非常重要的)实例之外,文字代码和评估代码是等效的:符号和表达式(地图,向量,集合,字符串,关键字,数字,布尔值,等等,都将“自我评估”)。
user=> 1 ;evaluates to itself
1
user=> "abc" ;evaluates to itself
"abc"
user=> :xyz ;evaluates to itself
:xyz
user=> [1 "abc" :xyz] ;evaluates to itself
[1 "abc" :xyz]
相反:
user=> (+ 1 2) ;an expression evaluates to not itself
3
user=> Math/PI ;a symbol evaluates to not itself
3.141592653589793
user=> + ;another example, a little weirder
#<core$_PLUS_ clojure.core$_PLUS_@417ffb28>
假设你想要创建some-fn-or-macro
来表现得像这样:
user=> (some-fn-or-macro (get {:a 10 :b 20} :a))
"(get {:a 10 :b 20} :a)"
user=> (some-fn-or-macro +)
"+"
您无法使用某项功能执行此操作。试试吧:
user=> (defn some-fn-or-macro [expr] (str expr))
#'user/some-fn-or-macro
user=> (some-fn-or-macro (get {:a 10 :b 20} :a))
"10"
这里发生了什么? some-fn-or-macro
的参数(即expr
)在被字符串化之前得到了评估。但是,如果我们所做的只是将函数的定义更改为宏,那么一切都会很棒:
user=> (defmacro some-fn-or-macro [expr] (str expr))
#'user/some-fn-or-macro
user=> (some-fn-or-macro (get {:a 10 :b 20} :a))
"(get {:a 10 :b 20} :a)"
话虽如此,如果我们再次采用原始函数定义,并简单引用调用的参数,那也有效:
user=> (defn some-fn-or-macro [expr] (str expr))
#'user/some-fn-or-macro
user=> (some-fn-or-macro '(get {:a 10 :b 20} :a))
"(get {:a 10 :b 20} :a)"
因此,如果您的用例要求参数保持字面值/未评估,那么您只需要需要来编写宏。如果你可以控制你的工具的使用方式(我猜测它总是很少),你可以决定开发一个函数,并指示用户根据需要引用参数。
***注意:我如何使用上面的宏可能会让你陷入关于宏的一个非常重要的事实:他们的输出得到评估。例如:
user=> (defmacro example-macro [] '(+ 1 2))
#'user/example-macro
user=> (example-macro)
3
你可能认为这很奇怪。有几种方法可以理解它。宏希望将源代码作为输入,因此他们将源代码作为输出是很自然的 - 源代码需要在某些时候进行评估。实际上,我倾向于将宏和函数之间的区别视为“移位评估” - 评估发生在“调用之前”,参数(函数);或“后”调用,输出(对于宏)。