gpt4 book ai didi

macros - 如何在 `defmacro` 中正确使用语法引用和取消引用

转载 作者:行者123 更新时间:2023-12-04 17:19:44 24 4
gpt4 key购买 nike

我有一个简单的宏:

(defmacro macrotest [coll]
`(let [result# ~(reduce + coll)]
result#))

为什么,如果此代码有效:
(macrotest [1 2 3])

这段代码不起作用吗?
(def mycoll [1 2 3])
(macrotest mycoll)

最佳答案

符号与其指向的值不同。

关于宏的重要考虑因素之一不仅是它们如何创建新代码,还包括它们如何处理您传入的参数。

考虑一下:
(def v [1 2 3])(somefunction v)
参数 v传递给这个函数的不是作为符号到达函数内部的,v .相反,因为这是一个函数调用,首先评估参数,然后将它们的结果值传递给函数。所以函数会看到[1 2 3] .

但是宏不是这样的,虽然很容易忘记。你打电话时:
(somemacro v)v未评估且未通过 [1 2 3] .在宏里面,你得到的只是一个符号,v .现在,您的宏可以在宏创建的代码中发出您传入的符号,但它不能对 v 的值执行任何操作。除非您使用 eval 添加额外的评估级别(见脚注——不推荐)。

当您取消引用宏参数时,您将获得一个符号,而不是一个值。

考虑这个例子:

user> (def a 5)
#'user/a
user> (defmacro m [x] `(+ ~x ~x))
#'user/m
user> (m a)
10
user> (macroexpand '(m a))
(clojure.core/+ a a)
user> (defmacro m [x] `~(+ x x))
#'user/m
user> (m a)
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126)

这就像在 REPL 上尝试这样做: (+ 'a 'a)添加符号,而不是添加这些符号指向的值。

您可能会查看宏 m 的这两个不同定义。并认为他们在做同样的事情。但是在第二个中,尝试对 做一些事情。符号 x ,用它做加法。在第一个宏中,编译器不会被要求对 x 做任何事情。它只是被告知发出加法操作,然后将在运行时实际处理。

请记住,宏的目的是创建代码,而不是执行函数会执行的运行时事件。然后立即运行创建的代码(通过隐式 eval ),因此很容易忘记这种分离,但它们是两个不同的步骤。

作为最后的练习,假设您是一名编译器。我想让你现在告诉我这段代码的结果值是什么:
(* t w)
好?不可能。你无法告诉我这个问题的答案,因为你不知道是什么 tw是。但是,稍后,在运行时,当这些可能具有值时,这将很容易。

这就是宏的作用:它们输出内容供运行时处理。

(非常不推荐,但为了帮助进一步描述正在发生的事情,这是有效的:)

用户>(定义为 1)

用户> (defmacro m [x] `~(+ (eval x) (eval x)))

用户> (m a)

2

关于macros - 如何在 `defmacro` 中正确使用语法引用和取消引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24356396/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com