我想編寫一個宏@unpack t
,它將一個對象t
並將其所有字段複製到本地作用域中。例如,給定Julia宏擴展命令
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
的擴展尚未解析,因此失敗。 - 如果我們不會在第2步中失敗,編譯器現在將解析
@unpackable
的擴展。
這種情況可以防止在源文件中使用@unpackable
。有沒有什麼辦法告訴編譯器交換上面列表中的第2步和第3步?
的背景,這個問題是我工作的迭代求解中的https://gist.github.com/jiahao/9240888精神的基於迭代器的實現。像MinRes這樣的算法在相應的狀態對象(當前爲8)中需要相當多的變量,並且我不想每次在變量中使用例如state.variable
。 next()
功能,我也不想手動複製所有這些功能,因爲這會使代碼變得越來越難以維護。最後,這主要是元編程的練習。
我認爲你可以更簡單地使用生成的函數來做到這一點。但是用例是什麼? –
我不認爲生成的函數將起作用,因爲函數引入了新的變量作用域,並且只能在該作用域內運行。我會添加一些關於問題背景的評論。 – gTcV
實際上,現在我認爲它可能會更合理的在我的用例中放棄狀態類型並使用元組來代替,爲此我可以通過元組拆包(即'i, x =(42,pi)')。 – gTcV