当我尝试使用矩阵作为字段来创建复合类型时,堆栈溢出

Cha*_*own 4 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])
Run Code Online (Sandbox Code Playgroud)

我使用临时变量e_ij来制作矩阵,因为我不希望复合Tables变体是可变的.所以,我的理由是,通过首先在虚拟变量中生成表格e_ij,然后我可以初始化Tables我真正想要的不可变量.

如果我注释掉结构的外部构造函数,Tables它就可以工作.但是,我实际上希望有几个不同的外部构造函数用于不同字段未传递数据以进行初始化的情况.在这些情况下,我想给它们默认矩阵.

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

DNF*_*DNF 5

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

struct Tables
    eij::Array{Float64,2}
end
Run Code Online (Sandbox Code Playgroud)

相当于:

struct Tables
    eij::Array{Float64,2}
    Tables(eij::Array{Float64,2}) = new(eij)
end
Run Code Online (Sandbox Code Playgroud)

当你定义这个外部构造

Tables(eij::Array{Float64,2}) = Tables(eij)
Run Code Online (Sandbox Code Playgroud)

你妨碍了内部构造函数.您的外部构造函数只是递归调用自身,直到您获得堆栈溢出.

另一方面,这样做,

Tables(eij) = Tables(eij)
Run Code Online (Sandbox Code Playgroud)

实际上相当于这个:

Tables(eij::Any) = Tables(eij)
Run Code Online (Sandbox Code Playgroud)

所以当你随后打电话

vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])
Run Code Online (Sandbox Code Playgroud)

然后它只是忽略你的外部构造函数,因为有一个更具体的方法匹配,即内部构造函数.因此,特定的外部构造函数是无用的,它将被忽略,或者它会递归直到堆栈溢出.

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

Tables() = Tables(zero(5, 5))
Run Code Online (Sandbox Code Playgroud)

应该管用.

我可能会这样做,但是:

struct Tables
    eij::Array{Float64,2}
    Tables(eij=zeros(5, 5)) = new(eij)
end
Run Code Online (Sandbox Code Playgroud)

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

struct Tables
    eij::Array{Float64,2}
    sij::Array{Float64,2}
    Tables(eij=zeros(5,5), sij=zeros(5,5)) = new(eij, sij)
end
Run Code Online (Sandbox Code Playgroud)

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


Bog*_*ski 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
Run Code Online (Sandbox Code Playgroud)

而定义内部构造函数会抑制外部构造函数的定义:

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
Run Code Online (Sandbox Code Playgroud)

因此案例并非100%相同.自动生成的外部构造函数的目的是在可能的情况下执行其参数的自动转换,参见eg(这是第一种情况下的结果 - 当没有定义内部构造函数时):

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
)
Run Code Online (Sandbox Code Playgroud)

而在第二种情况下,同一个调用会抛出一个方法错误.