在Julia

时间:2019-01-09 23:57:08

标签: struct julia

说我有一个结构体:

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])

如何将值仅推入结构的一个元素?

2 个答案:

答案 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真的需要可变吗?如果只希望对data1data2中的数组进行突变,那么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[])