当我尝试从另一个先前存在的结构对象构建结构对象(其中一个参数依赖于其他参数)时,我得到一个
StackOverflowError
。
我定义了一个如下所示的类型:
using Parameters
julia> @with_kw struct A
a1 = 0
a2 = 0
b = (; b1 = a1, b2 = a2)
end
由此我可以构建一个对象
A
:
julia> anobject = A(; a1 = 1, a2 = 3)
有:
julia> anobject.b = (; b1 = 1, b2 = 3)
现在,我需要从第一个对象构建第二个对象,并更新一些值:
julia> object2 = A(anobject; a1 = 5)
但这会导致:
julia> object2.a1 = 5
julia> object2.a2 = 3
julia> object2.b = (; b1 = 1, b2 = 3)
b
尚未更新。
我尝试定义一个构造函数:
julia> A(a::A; a1 = 0, kw...) = A(a; a1 = a1, b = (; a.b..., b1 = a1), kw...)
但是使用它时,会产生
StackOverflowError
。
julia> object3 = A(anobject; a1 = 5)
ERROR: StackOverflowError:
当然我不明白它来自哪里,也没有找到任何相关文档。正确的方法是什么?
您的
A
构造函数正在调用自身,而不是使用关键字调用默认的 A
构造函数。
一个快速更改是使用关键字调用默认的
A
构造函数:
import Parameters: @with_kw
@with_kw struct A
a1 = 0
a2 = 0
b = (; b1 = a1, b2 = a2)
end
A(a::A; a1 = 0) = A(a1 = a1, a2 = a.a2, b = (; a.b..., b1 = a1))
obj1 = A(; a1 = 1, a2 = 3)
# A
# a1: Int64 1
# a2: Int64 3
# b: @NamedTuple{b1::Int64, b2::Int64}
obj1.b
# (b1 = 1, b2 = 3)
obj2 = A(obj1; a1 = 5)
# A
# a1: Int64 5
# a2: Int64 3
# b: @NamedTuple{b1::Int64, b2::Int64}
obj2.b
# (b1 = 5, b2 = 3)
一个复杂的更改是使用像
NamedTupleTools
这样的包将旧结构转换为 NamedTuple
,将其与新的关键字参数合并,然后使用这些关键字调用默认的 A
构造函数。
import Parameters: @with_kw
import NamedTupleTools: merge, ntfromstruct
@with_kw struct A
a1 = 0
a2 = 0
b = (; b1 = a1, b2 = a2)
end
function A(a::A; kw...)
a_nt = merge(ntfromstruct(a), NamedTuple(kw))
b_nt = (; b1 = a_nt.a1, b2 = a_nt.a2)
A(; merge(a_nt, (; b = b_nt))...)
end
obj1 = A(; a1 = 1, a2 = 3)
# A
# a1: Int64 1
# a2: Int64 3
# b: @NamedTuple{b1::Int64, b2::Int64}
obj1.b
# (b1 = 1, b2 = 3)
obj2 = A(obj1; a1 = 5)
# A
# a1: Int64 5
# a2: Int64 3
# b: @NamedTuple{b1::Int64, b2::Int64}
obj2.b
# (b1 = 5, b2 = 3)