我想编写一个@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
我认为问题是编译器尝试按以下步骤进行
@unpackable
但不解析它。@unpackFoo
失败,因为@unpackable
尚未解析的扩展。@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.jl
或include("foo.jl")
,它将运行正常。就像begin
上面的代码块一样,您不能在同一代码块中具有宏定义及其使用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句