说我有一个结构体:
mutable struct DataHolder
data1::Vector{Float64}
data2::Vector{Float64}
function DataHolder()
emp = Float64[]
new(emp, emp)
end
end
d = DataHolder()
当我尝试通过以下方式将值仅推入结构d
的一个元素时:
push!(d.data1, 1.0)
不仅将值推入d.data1
,还会推d.data2
。确实,REPL说
julia> d
DataHolder([1.0], [1.0])
如何将值仅推入结构的一个元素?
答案 0 :(得分:3)
您的问题不在push!
中,而在DataHolder
的内部构造函数中。具体来说:
emp = Float64[]
new(emp, emp)
此代码模式意味着新DataHolder
的字段都指向同一数组(在内存中)。因此,如果您突变其中一个(例如,通过push!
),那么您也会同时突变另一个。
您可以将这两行替换为:
new(Float64[], Float64[])
获得所需的行为。
更一般地,尽管不是禁止的,但是您对内部构造函数的使用有点奇怪。通常,内部构造函数应具有与您的结构字段完全对应的方法签名,并且内部构造函数本身通常仅用于提供一组新的DataHolder
应该接受的通用测试。
我个人将按照以下方式重新编写您的代码:
mutable struct DataHolder
data1::Vector{Float64}
data2::Vector{Float64}
function DataHolder(data1::Vector{Float64}, data2::Vector{Float64})
#Any tests on data1 and data2 go here
new(data1, data2)
end
end
DataHolder() = DataHolder(Float64[], Float64[])
如果您不需要对DataHolder
进行任何通用测试,请完全删除内部构造函数。
最后的思考:DataHolder
真的需要可变吗?如果只希望对data1
和data2
中的数组进行突变,那么DataHolder
本身就不需要可变,因为这些数组已经可变。如果您计划完全重新分配这些字段中的值,则只需要DataHolder
是可变的,例如dh.data1 = [2.0]
形式的操作。
评论后更新:
我个人认为DataHolder() = DataHolder(Float64[], ..., Float64[])
没有任何问题。这只是一行代码,您无需再考虑它。或者,您可以执行以下操作:
DataHolder() = DataHolder([ Float64[] for n = 1:10 ]...)
只是将空向量的向量喷入构造函数参数中。
答案 1 :(得分:0)
@ColinTBowers回答了您的问题。这是一个非常简单且更通用的实现:
struct DataHolder # add mutable if you really need it
data1::Vector{Float64}
data2::Vector{Float64}
end
DataHolder() = DataHolder([], [])
也许您想允许使用Float64
以外的其他类型(因为为什么不呢?!):
struct DataHolder{T}
data1::Vector{T}
data2::Vector{T}
end
DataHolder{T}() where {T} = DataHolder{T}([], [])
DataHolder() = DataHolder{Float64}() # now `Float64` is the default type.
现在您可以执行以下操作:
julia> DataHolder{Rational}()
DataHold{Rational}(Rational[], Rational[])
julia> DataHolder()
DataHold{Float64}(Float64[], Float64[])