多看文档,关于卫生宏的内容
julia> macro swap(a,b)
:(($a,$b)=($b,$a))|>esc
end
@swap (macro with 1 method)
julia> (a,b)=(1,2)
(1, 2)
julia> @swap a b
(2, 1)
julia> (a,b)
(2, 1)
建议稍微看一下 Markdown Reference。
截图还是有所不便。
这样输入代码
```julia
julia> # 代码写在这里
julia> macro swap(a,b)
temp = a
a = b
b = temp
end
@swap (macro with 1 method)
julia>
```
显示为
julia> # 代码写在这里
julia> macro swap(a,b)
temp = a
a = b
b = temp
end
@swap (macro with 1 method)
julia>
你要修改宏调用所在环境的变量,这个宏就“不卫生”了,需要转义一下,让相关的变量在宏调用所处环境中解析。
macro swap(x, y)
quote
local temp = $x
$x = $y
$y = temp
end |> esc
end
效果
julia> a, b = 1, 2
(1, 2)
julia> a, b
(1, 2)
julia> macro swap(x, y)
quote
local temp = $x
$x = $y
$y = temp
end |> esc
end
@swap (macro with 1 method)
julia> macroexpand(Main, :(@swap(a,b)))
quote
#= REPL[75]:3 =#
local temp = a
#= REPL[75]:4 =#
a = b
#= REPL[75]:5 =#
b = temp
end
julia> @swap(a, b)
1
julia> a,b
(2, 1)
你提到的另一种方法
macro swap2(x, y)
quote
$x,$y = $y,$x
end |> esc
end
效果
julia> a, b = 1, 2
(1, 2)
julia> macro swap2(x, y)
quote
$x,$y = $y,$x
end |> esc
end
@swap2 (macro with 1 method)
julia> macroexpand(Main, :(@swap2(a,b)))
quote
#= REPL[97]:3 =#
(a, b) = (b, a)
end
julia> @swap2(a, b)
(2, 1)
julia> a, b
(2, 1)
thank you both
有些疑问
当初设计宏的时候为什么不直接把宏做成转义
设计如此。宏是在代码parse阶段的AST的函数(输入AST,输出变换后的AST)。为了保证宏的引入不会给你的环境造成奇怪的影响,你在函数里使用的变量,都会被gensym
生成一个不和其他变量冲突的名字。如果你就是要影响当前环境,就需要显式esc
。
要的就是显式,让你知道你自己在做什么。这就和因为Java默认内部类非静态,容易导致内存泄漏,所以Kotlin干脆让你默认静态,想要非静态请手动inner class
一样,没啥可问的。
1 个赞
推荐的做法是什么,显式调用esc吗
首先,因为宏是对AST的变换,你拿到的只是一个个符号,没有任何的上下文环境、类型信息等。如果你确实需要根据传入的:a去操作当前名为a的变量,你就esc呗
从一个系统/语言的设计(design)的原则上来,为了降低人犯错的概率,如果有得选,操作代价最小、最顺手的操作应该是最安全的那种。在工业界你能找到很多例子(比如为什么刹车比油门踏板大这么多?)