为了帮助我的调试(也是为了更好地理解Julia宏是如何工作的),我正在尝试定义一个简单的宏,它使用“进入”和“离开”通知来源化代码块。这是我到目前为止想出的:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$expr
println("Leaving $title")
end
end乍一看,它似乎做了我想做的事情:
julia> @dbg "first test" begin
println("does it work?")
end
Entering first test
does it work?
Leaving first test然而,一旦涉及到变量,就什么都不能用了,我对所有变量的访问都会得到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我做错了什么?
发布于 2020-02-24 18:29:20
简而言之,您错过了macro hygiene的概念,尤其是esc函数。
尽管文档的这一部分对于任何想要开发自己的宏的人来说都是很好的读物,但让我们试着扩展一下这个特定示例中宏卫生的作用,以及如何修复这些问题。
@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抛开#= ... =#标记中的所有注释不谈,我们看到的几乎就是我们想要得到的代码:用户代码块被打印“进入”和“离开”通知的语句包围。然而,有一个显著的区别:像foo或title这样的变量名称已经被像var"#33#foo"或var"#32#title"这样看起来怪异的名称所取代。这就是所谓的"macro hygiene",它有助于避免宏本身中使用的变量(如本例中的title )与作为参数提供给宏的代码块中使用的变量(如此处的foo )之间可能发生的冲突。(例如,考虑一下如果在定义title变量的代码块上使用@dbg会发生什么。)
Julia在谨慎方面出错,并以这种方式保护宏中出现的所有变量。如果要对宏生成的表达式的选定部分禁用此功能,可以将这些子表达式包装在esc函数中。例如,在您的示例中,应该转义用户提供的表达式:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$(esc(expr))
println("Leaving $title")
end
end现在,事情应该像他们想要的那样工作:
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 https://stackoverflow.com/questions/60373893
复制相似问题