我创建了一个包含大量参数的复杂计算模型。由于我需要运行很多场景,所以我决定将所有这些输入参数包装成一个巨大的
struct
:
using Parameters
@with_kw struct MyModel
a::Int = 5
b::Float64 = 5.5
c::Matrix{Float64} = rand(3,4)
# 40 other parameters go here
end
我有一个对象
m
作为示例:
m = MyModel(a=15)
现在,在编写数学代码时,我不想在每个符号前面写
m.
。因此我需要将结构体字段放入局部变量中。一种方法是使用 @unpack
宏:
@unpack a, b, c = m
对于我想在各种函数中解压的巨大结构体,这很不方便(请注意,我的结构体有大约 40 个字段)。如何解压该结构而不花费时间并用所有这些参数使我的代码混乱?
Parameters.jl 中的宏
@with_kw
为此目的定义了一个宏:
julia> using Parameters
julia> @with_kw struct MyModel # exactly as in the question
a::Int = 5
b::Float64 = 5.5
c::Matrix{Float64} = rand(3,4)
# 40 other parameters go here
end
MyModel
julia> @macroexpand @unpack_MyModel x
quote
a = x.a
b = x.b
c = x.c
end
因此,当您知道
@unpack_MyModel m
时,写 @unpack a, b, c = m
就相当于写 m isa MyModel
。
另一个选项是StaticModules.jl。这是我从该包的自述文件中复制并粘贴的示例:
julia> struct Bar
a
b
end
julia> @with Bar(1, 2) begin
a^2, b^2
end
(1, 4)
对于自定义情况,您可以为您的类型制作专用宏,对于其他情况,请参阅上面的答案:
macro unpack_MyModel(q)
code = Expr(:block, [ :($field = $q.$field) for field in fieldnames(MyModel) ]...)
esc(code)
end
这只是插入以下代码:
julia> @macroexpand @unpack_MyModel(m)
quote
a = m.a
b = m.b
c = m.c
end
该宏可以在任何函数内部使用,例如:
function f(m::MyModel)
@unpack_MyModel(m)
return a+b
end
我认为有一种更惯用的方法,不需要宏或任何其他包。该文档建议了最惯用的方法,例如矩阵分解的文档。
特别是这一行:
julia> l, u, p = lu(A); # destructuring via iteration
这表明迭代是正确的方法。所以剩下的就是让你的结构实现迭代接口
你的结构看起来像这样
struct MyModel
a::Int = 5
b::Float64 = 5.5
c::Matrix{Float64} = rand(3,4)
# 40 other parameters go here
end
要实现迭代接口,您需要定义
Base.iterate
,它将 MyModel
作为参数和“状态”。该函数返回与状态相对应的元素,并调用下一次迭代(有点像链表)。例如:
function Base.iterate(m::MyModel, state)
if state == 1
return(m.a, state+1)
elseif state == 2
return(m.b, state+1
elseif state == 3
return(m.c, state+1)
else
return nothing
end
end
在 Julia 中,迭代在
next(iter) == nothing
时停止,这就是为什么当没有什么可以迭代时必须返回 nothing
。
您可以在 lu
分解的
源代码中找到更惯用(但人为的)示例:
# iteration for destructuring into components
Base.iterate(S::LU) = (S.L, Val(:U))
Base.iterate(S::LU, ::Val{:U}) = (S.U, Val(:p))
Base.iterate(S::LU, ::Val{:p}) = (S.p, Val(:done))
Base.iterate(S::LU, ::Val{:done}) = nothing
如果我没记错的话,它使用
Val
进行一些编译时优化。
在具有 40 个字段的结构的特殊情况下,我想不出更符合人体工程学的方式,也许数据更适合另一种存储选项。
我刚刚开始学习 Julia,我正在阅读此网页 https://www.matecdev.com/posts/julia-structs.html
在“解构结构”标题下,它的代码类似于:
using Parameters
@with_kw struct MyModel
a :: Int = 5
b :: Float64 = 5.5
c :: Matrix{Float64} = rand(3,4)
# 40 other parameters go here
end
m = MyModel()
(; a, b, c) = m
println(a)