朱莉娅宏扩展顺序

病毒

我想编写一个@unpack t接受对象t并将其所有字段复制到本地范围的宏例如,给定

immutable Foo
    i::Int
    x::Float64
end
foo = Foo(42,pi)

该表达式@unpack foo应扩展为

i = foo.i
x = foo.x

不幸的是,这样的宏不存在,因为它必须知道所传递对象的类型。为了规避此限制,我引入了@unpackFoo foo具有相同效果的特定于类型的宏,但是由于我很懒,所以我希望编译器@unpackFoo为我编写所以我将类型定义更改为

@unpackable immutable Foo
    i::Int
    x::Float64
end

应该扩展为

immutable Foo
    i::Int
    x::Float64
end
macro unpackFoo(t)
    return esc(quote
        i = $t.i
        x = $t.x
    end)    
end

写作@unpackable并不难:

macro unpackable(expr)
    if expr.head != :type
        error("@unpackable must be applied on a type definition")
    end

    name = isa(expr.args[2], Expr) ? expr.args[2].args[1] : expr.args[2]
    fields = Symbol[]
    for bodyexpr in expr.args[3].args
        if isa(bodyexpr,Expr) && bodyexpr.head == :(::)
            push!(fields,bodyexpr.args[1])
        elseif isa(bodyexpr,Symbol)
            push!(fields,bodyexpr)
        end
    end

    return esc(quote
        $expr
        macro $(symbol("unpack"*string(name)))(t)
            return esc(Expr(:block, [:($f = $t.$f) for f in $fields]...))
        end
    end)
end

在REPL中,此定义可以正常工作:

julia> @unpackable immutable Foo
           i::Int
           x::Float64
       end

julia> macroexpand(:(@unpackFoo foo))
quote 
    i = foo.i
    x = foo.x
end

如果我将放在与@unpackFoo相同的编译单元中,则会出现问题@unpackable

julia> @eval begin
       @unpackable immutable Foo
           i::Int
           x::Float64
       end
       foo = Foo(42,pi)
       @unpackFoo foo
       end
ERROR: UndefVarError: @unpackFoo not defined

我认为问题是编译器尝试按以下步骤进行

  1. 扩展@unpackable但不解析它。
  2. 尝试扩展@unpackFoo失败,因为@unpackable尚未解析的扩展
  3. 如果我们不会在步骤2失败,则编译器现在将解析的扩展@unpackable

在这种情况下,无法@unpackable在源文件中使用它。有什么办法告诉编译器交换上面列表中的步骤2和3.?


这个问题的背景是我本着https://gist.github.com/jiahao/9240888的精神致力于基于迭代器的迭代求解器实现诸如MinRes之类的算法在相应的状态对象(当前为8个)中需要相当多的变量,并且我既不想state.variable每次在next()函数中使用变量时都编写该变量,也不想在此过程中手动复制所有变量代码,很难维护。最后,这主要是元编程中的一个练习。

一分钟

首先,我建议将其编写为:

immutable Foo
  ...
end

unpackable(Foo)

whereunpackable是一个接受类型的函数,构造适当的表达式并eval对其进行处理。这有两个优点,例如,您可以将其应用到任何类型,而无需在定义时将其固定,并且事实是不必对类型声明进行大量解析(您可以调用fieldnames(Foo) == [:f, :i]并与之合作)。

其次,虽然我不了解您的用例的详细信息(并且不喜欢通用规则),但我会警告人们对此种说法不屑一顾。由于引入了非本地依赖关系,因此使代码更难阅读。突然,为了知道x是局部变量还是全局变量,您必须在一个完全不同的文件中查找类型的定义。更好,更通用的方法是显式解压缩变量,这可通过@destruct在MacroTools.jl中使用

@destruct _.(x, i) = myfoo
# now we can use x and i

(您也可以破坏嵌套的数据结构和可索引对象,这很好。)

要回答您的问题:关于Julia如何运行代码(s / parse / evaluate),您本质上是正确的。整个块将一起解析,扩展和评估,这意味着在您的示例中,您试图@unpackFoo在定义之前进行扩展

但是,在加载.jl文件时,Julia一次评估一个块中的块,而不是一次评估所有块。

这意味着您可以愉快地编写如下文件:

macro foo()
  :(println("hi"))
end

@foo()

并运行julia foo.jlinclude("foo.jl"),它将运行正常。就像begin上面的代码块一样,您不能在同一代码块中具有宏定义及其使用

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章