首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Julia宏中的`UndefVarError`

Julia宏中的`UndefVarError`
EN

Stack Overflow用户
提问于 2020-02-24 18:29:20
回答 1查看 131关注 0票数 1

为了帮助我的调试(也是为了更好地理解Julia宏是如何工作的),我正在尝试定义一个简单的宏,它使用“进入”和“离开”通知来源化代码块。这是我到目前为止想出的:

代码语言:javascript
复制
macro dbg(block_title, expr)
    quote
        title = $block_title
        println("Entering $title")
        $expr
        println("Leaving  $title")
    end
end

乍一看,它似乎做了我想做的事情:

代码语言:javascript
复制
julia> @dbg "first test" begin
           println("does it work?")
       end
Entering first test
does it work?
Leaving  first test

然而,一旦涉及到变量,就什么都不能用了,我对所有变量的访问都会得到UndefVarError。看起来宏内部和外部的作用域是不同的:

代码语言:javascript
复制
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

我做错了什么?

EN

回答 1

Stack Overflow用户

发布于 2020-02-24 18:29:20

简而言之,您错过了macro hygiene的概念,尤其是esc函数。

尽管文档的这一部分对于任何想要开发自己的宏的人来说都是很好的读物,但让我们试着扩展一下这个特定示例中宏卫生的作用,以及如何修复这些问题。

@macroexpand提供了一种调试宏的有用方法

代码语言:javascript
复制
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

抛开#= ... =#标记中的所有注释不谈,我们看到的几乎就是我们想要得到的代码:用户代码块被打印“进入”和“离开”通知的语句包围。然而,有一个显著的区别:像footitle这样的变量名称已经被像var"#33#foo"var"#32#title"这样看起来怪异的名称所取代。这就是所谓的"macro hygiene",它有助于避免宏本身中使用的变量(如本例中的title )与作为参数提供给宏的代码块中使用的变量(如此处的foo )之间可能发生的冲突。(例如,考虑一下如果在定义title变量的代码块上使用@dbg会发生什么。)

Julia在谨慎方面出错,并以这种方式保护宏中出现的所有变量。如果要对宏生成的表达式的选定部分禁用此功能,可以将这些子表达式包装在esc函数中。例如,在您的示例中,应该转义用户提供的表达式:

代码语言:javascript
复制
macro dbg(block_title, expr)
    quote
        title = $block_title
        println("Entering $title")
        $(esc(expr))
        println("Leaving  $title")
    end
end

现在,事情应该像他们想要的那样工作:

代码语言:javascript
复制
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 
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60373893

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档