ffe*_*tte 1 metaprogramming julia
为了帮助我的调试(也为了更好地理解 Julia 宏的工作原理),我试图定义一个简单的宏,用“进入”和“离开”通知环绕代码块。这是我到目前为止想出的:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$expr
println("Leaving $title")
end
end
Run Code Online (Sandbox Code Playgroud)
乍一看,它似乎做我想要的:
julia> @dbg "first test" begin
println("does it work?")
end
Entering first test
does it work?
Leaving first test
Run Code Online (Sandbox Code Playgroud)
然而,一旦涉及到变量,就没有任何作用了,我得到
UndefVarError了所有变量的访问。看起来宏内部和外部的范围是不同的:
julia> @dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
Entering initialization
foo = [0.9178016919066918, 0.6004694971609528, 0.5294790810682284, 0.04208146400653634, 0.09271603217172952, 0.2809448815925, 0.68236281020963, 0.8313876607106496, 0.07484095574744898, 0.14099531301938573]
Leaving initialization
julia> foo
ERROR: UndefVarError: foo not defined
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
尽管对于想要开发自己的宏的任何人来说,文档的这一部分都值得一读,但让我们尝试扩展一下这个特定示例中宏卫生的作用,以及如何修复这些问题。
调试宏的有用方法由@macroexpand以下提供
:
julia> @macroexpand @dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
quote
#= REPL[1]:3 =#
var"#32#title" = "initialization"
#= REPL[1]:4 =#
Main.println("Entering $(var"#32#title")")
#= REPL[1]:5 =#
begin
#= REPL[5]:2 =#
var"#33#foo" = Main.rand(10)
#= REPL[5]:3 =#
Main.println("foo = ", var"#33#foo")
end
#= REPL[1]:6 =#
Main.println("Leaving $(var"#32#title")")
end
Run Code Online (Sandbox Code Playgroud)
撇开#= ... =#标记中的所有注释不谈,我们几乎可以看到我们想要获得的代码:用户代码块已被打印“进入”和“离开”通知的语句包围。但是,有一个显着的区别:变量名如foo或title已被替换为看起来很奇怪的名称,如var"#33#foo"或var"#32#title"。这就是所谓的“宏卫生”,它有助于避免宏本身中使用的变量(如title本例中)与作为宏参数提供的代码块中使用的变量(如foo这里)。(想想如果你@dbg在定义title变量的代码块上使用你会发生什么。)
Julia 谨慎行事,并以这种方式保护出现在宏中的所有变量。如果要对宏生成的表达式的选定部分禁用此esc
功能,可以将这些子表达式包装在
函数中。在您的示例中,您应该例如转义用户提供的表达式:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$(esc(expr))
println("Leaving $title")
end
end
Run Code Online (Sandbox Code Playgroud)
现在事情应该像希望他们一样工作:
julia> @dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
Entering initialization
foo = [0.2955287439482881, 0.8989053281359838, 0.27751430906108343, 0.4920810199867245, 0.7633806735297282, 0.34535540650110597, 0.7099231627594489, 0.39978144801175564, 0.9104888704503833, 0.1983996781283539]
Leaving initialization
julia> @dbg "computation" begin
foo .+= 1
end
Entering computation
Leaving computation
julia> foo
10-element Array{Float64,1}:
1.295528743948288
1.8989053281359838
1.2775143090610834
1.4920810199867245
1.7633806735297282
1.345355406501106
1.709923162759449
1.3997814480117556
1.9104888704503833
1.198399678128354
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
108 次 |
| 最近记录: |