当我尝试使用矩阵作为字段的复合类型时出现堆栈溢出

时间:2019-02-05 06:09:35

标签: julia

在下面的代码中,我有一个复合类型,在我的真实代码中,几个字段是矩阵。在这个例子中只有1。当我尝试构造复合类型时,我一直在堆栈溢出。这是代码示例:

struct Tables
    eij::Array{Float64,2}
end

Tables(eij::Array{Float64,2}) = Tables(eij) # works if this is commented out
x = 5   # arbitrary matrix dimension    
e_ij = Array{Float64,2}(undef,x,x)

for i=1:x
    for j=1:x
        e_ij[i,j] = i*j/2.3     #dummy numbers, but not the case in real code
    end
end

vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])

由于我不希望复合e_ij可变,因此我先使用临时变量Tables来制作矩阵。因此,我的理由是,通过首先在诸如e_ij之类的虚拟变量中生成表,然后可以初始化我真正想要的不可变Tables

如果我注释掉结构Tables的外部构造函数,它将起作用。但是,对于不同的字段没有传递要初始化的数据的情况,我实际上希望有几个不同的外部构造函数。在这种情况下,我想给他们默认矩阵。

我得到的错误如下:ERROR: LoadError: StackOverflowError: on vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])

2 个答案:

答案 0 :(得分:5)

定义复合类型时,将自动定义内部构造函数,因此:

struct Tables
    eij::Array{Float64,2}
end

等效于此:

struct Tables
    eij::Array{Float64,2}
    Tables(eij::Array{Float64,2}) = new(eij)
end

当您定义 this 外部构造函数

Tables(eij::Array{Float64,2}) = Tables(eij)

您会妨碍内部构造函数。您的外部构造函数只是递归地调用自身,直到出现堆栈溢出。

另一方面,

Tables(eij) = Tables(eij)

实际上等效于此:

Tables(eij::Any) = Tables(eij)

所以当您随后致电

vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])

然后它会忽略您的外部构造函数,因为存在一个更特定的方法匹配项,即 inner 构造函数。因此,特定的外部构造函数非常无用,它将被忽略,或者将递归直到堆栈溢出。

最简单的解决方案是:只是不创建外部构造函数。如果要做需要外部条件来强制执行某些条件,请确保它不会因为具有相同的类型签名而遮盖内部结构。例如,

Tables() = Tables(zero(5, 5))

应该工作。

不过,我可能会这样做:

struct Tables
    eij::Array{Float64,2}
    Tables(eij=zeros(5, 5)) = new(eij)
end

对于第二个示例,有两个字段,您可以尝试以下操作:

struct Tables
    eij::Array{Float64,2}
    sij::Array{Float64,2}
    Tables(eij=zeros(5,5), sij=zeros(5,5)) = new(eij, sij)
end

您的输入将转换为Float64矩阵,如果可能的话 ,否则将引发异常。

答案 1 :(得分:5)

DNF给出了适当的解释,所以+1。我只想添加一条小评论(不是问题的答案,而是我的经验相关的内容),对于评论来说太长了。

当您自己省略指定内部构造函数时,Julia会自动定义一个内部构造函数和一个外部构造函数:

julia> struct Tables
           eij::Array{Float64,2}
       end

julia> methods(Tables)
# 2 methods for generic function "(::Type)":
[1] Tables(eij::Array{Float64,2}) in Main at REPL[1]:2
[2] Tables(eij) in Main at REPL[1]:2

在定义内部构造函数时,将取消对外部构造函数的定义:

julia> struct Tables
           eij::Array{Float64,2}
           Tables(eij::Array{Float64,2}) = new(eij)
       end

julia> methods(Tables)
# 1 method for generic function "(::Type)":
[1] Tables(eij::Array{Float64,2}) in Main at REPL[1]:3

因此,案例并非100%等效。自动生成的外部构造函数的目的是在可能的情况下对其参数进行自动转换,请参见例如(这是第一种情况的结果-当未定义内部构造函数时):

julia> @code_lowered Tables([true false
       true false])
CodeInfo(
1 ─ %1 = (Core.apply_type)(Main.Array, Main.Float64, 2)
│   %2 = (Base.convert)(%1, eij)
│   %3 = %new(Main.Tables, %2)
└──      return %3
)

而在第二种情况下,相同的调用将引发方法错误。