代码之家  ›  专栏  ›  技术社区  ›  HiroIshida

在Julia中将值推送到struct元素时出现问题

  •  2
  • HiroIshida  · 技术社区  · 7 年前

    假设我有一个结构:

    mutable struct DataHolder
        data1::Vector{Float64}
        data2::Vector{Float64}
        function DataHolder()
            emp = Float64[]
            new(emp, emp)
        end
    end
    d = DataHolder()
    

    当我试图将一个值仅推送到struct的一个元素时 d 通过这样做:

    push!(d.data1, 1.0)
    

    该值不仅被推送 d.data1 而且 d.data2 . 事实上,REPL说

    julia> d
    DataHolder([1.0], [1.0])
    

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

    2 回复  |  直到 7 年前
        1
  •  3
  •   Colin T Bowers    7 年前

    push! ,而是在 DataHolder . 明确地:

    emp = Float64[]
    new(emp, emp)
    

    此代码模式意味着 数据持有者 两者都指向同一个数组(内存中)。所以如果你让其中一个变异(比如说,通过 推 ),你也会变异另一个。

    您可以将这两行替换为:

    new(Float64[], Float64[])
    

    更一般地说,虽然不禁止使用内部构造函数,但使用内部构造函数有点奇怪。通常,内部构造函数应该有一个与结构的字段完全对应的方法签名,并且内部构造函数本身通常只用于提供一组通用测试,任何新的 数据持有者 应该经历。

    就我个人而言,我将按照以下方式重新编写您的代码:

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

    如果你不需要做任何通用测试 数据持有者 ,然后完全删除内部构造函数。

    最后一个值得思考的问题:是吗 数据持有者 真的需要可变吗?如果您只想在 data1 data2 然后 数据持有者 它本身不需要是可变的,因为那些数组已经是可变的了。你只需要 如果您计划完全重新分配这些字段中的值,例如表单的操作,则为可变 dh.data1 = [2.0] .

    评论后更新: 就我个人而言,我不认为这有什么问题 DataHolder() = DataHolder(Float64[], ..., Float64[]) . 这只是一行代码,你再也不用想它了。或者,您可以执行以下操作:

    DataHolder() = DataHolder([ Float64[] for n = 1:10 ]...)
    

    它只是将一个由空向量组成的向量散播到构造函数参数中。

        2
  •  0
  •   DNF    7 年前

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