gpt4 book ai didi

Julia 宏展开顺序

转载 作者:行者123 更新时间:2023-12-01 15:00:33 26 4
gpt4 key购买 nike

我想写一个宏 @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)

其中 unpackable 是一个函数,它接受类型,构造适当的表达式并对其进行 eval。这有几个优点,例如您可以将它应用于任何类型而无需在定义时修复它,并且您不必对类型声明进行一堆解析(您可以调用 fieldnames(Foo) == [ :f, :i] 并使用它)。

其次,虽然我不详细了解您的用例(并且不喜欢笼统的规则),但我会警告说这种事情是不受欢迎的。它使代码更难阅读,因为它引入了非本地依赖性;突然之间,为了知道 x 是局部变量还是全局变量,您必须在整个不同的文件中查找类型的定义。一种更好、更通用的方法是显式解包变量,这可以通过 @destruct 宏在 MacroTools.jl 中使用:

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

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

回答您的问题:您基本上对 Julia 运行代码的方式(s/parse/evaluate)是正确的。整个 block 一起被解析、扩展和评估,这意味着在您的示例中,您试图在定义 @unpackFoo 之前对其进行扩展。

但是,在加载 .jl 文件时,Julia 会一次一个地计算文件中的 block ,而不是一次计算所有 block 。

这意味着你可以愉快地编写这样一个文件:

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

@foo()

然后运行 ​​julia foo.jlinclude("foo.jl") 它将运行良好。您不能像在上面的 begin block 中那样在同一个 block 中使用宏定义和它。

关于Julia 宏展开顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38646014/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com