【问题标题】:Gonum throws bad region panic when using an embedded struct使用嵌入式结构时,Gonum 会引发糟糕的区域恐慌
【发布时间】:2018-08-21 14:22:19
【问题描述】:

我正在使用 gonum 执行一些线性代数计算。在扩展原始 mat.VecDense 结构后,我在对其自身应用方法时遇到“坏区域:相同”恐慌。当我使用 gonum 提供的原始设置时,不会出现此错误。

这是我的实现:

type Vector struct {
    mat.VecDense
}
func NewVector(n int, data []float64) *Vector {
    return &Vector{*mat.NewVecDense(n, data)}
}

我正在使用以下 sn-p 对其进行测试:

func main() {
    u, v := mat.NewVecDense(3, []float64{1, 2, 3}), mat.NewVecDense(3, []float64{4, 5, 6})
    fmt.Printf("[U - NewVecDense]\tADDRESS: %v, VALUE: %v\n", &u, u)
    fmt.Printf("[V - NewVecDense]\tADDRESS: %v, VALUE: %v\n", &v, v)
    u.AddVec(u, v)

    fmt.Println("-------------------------")
    x, y := NewVector(3, []float64{1, 2, 3}), NewVector(3, []float64{4, 5, 6})
    fmt.Printf("[X - NewVector]\tADDRESS: %v, VALUE: %v\n", &x, x)
    fmt.Printf("[Y - NewVector]\tADDRESS: %v, VALUE: %v\n", &y, y)
    x.AddVec(x, y)
    fmt.Println(x)
}

虽然第一次添加执行良好,但第二次失败:

[U - NewVecDense]   ADDRESS: 0xc42000c028, VALUE: &{{[1 2 3] 1} 3}
[V - NewVecDense]   ADDRESS: 0xc42000c030, VALUE: &{{[4 5 6] 1} 3}
-------------------------
[X - NewVector] ADDRESS: 0xc42000c040, VALUE: &{{{[1 2 3] 1} 3}}
[Y - NewVector] ADDRESS: 0xc42000c048, VALUE: &{{{[4 5 6] 1} 3}}
panic: mat: bad region: identical

AddVec 是一个方法implemented by gonum

func (v *VecDense) AddVec(a, b Vector)

为什么会发生这种情况,请问正确的实现方式是什么?


编辑:

感谢@Himanshu,我设法解决了这个问题。

我为我正在使用的每个方法创建了传递方法,传递结构的正确级别:

type Vector struct {
    *mat.VecDense
}

func NewVector(n int, data []float64) Vector {
    return Vector{mat.NewVecDense(n, data)}
}

func (v *Vector) AddVec(a, b Vector) {
    v.VecDense.AddVec(a.VecDense, b.VecDense)
}

func (v *Vector) SubVec(a, b Vector) {
    v.VecDense.SubVec(a.VecDense, b.VecDense)
}

func (v *Vector) ScaleVec(alpha float64, a Vector) {
    v.VecDense.ScaleVec(alpha, a.VecDense)
}

func (v *Vector) AddScaledVec(a Vector, alpha float64, b Vector) {
    v.VecDense.AddScaledVec(a.VecDense, alpha, b.VecDense)
}

此外-我不确定这是否是正确的方法-我还将NewVector的返回类型从指向值的指针更改,因为它无论如何都包含指向mat.VecDense的指针。请注意,*mat.VecDense 满足 gonum 的 Vector 接口,因此将这个内部字段传递给方法可以正常工作,如上例所示。

【问题讨论】:

  • 请将 AddVec 函数的代码发布到您传递 Vector 结构的位置。
  • AddVec 由 gonum 实现:github.com/gonum/gonum/blob/master/mat/vector.go#L333。我会用这个参考更新主帖。
  • 可能我在做一些非常愚蠢的事情。但是,按照您的指示,我得到了以下实际可行的解决方案:func (v *Vector) AddVec(a *Vector, b *Vector) { v.VecDense.AddVec(&a.VecDense, &b.VecDense) }
  • 这是我在示例中提供的,用于使用具有嵌入式结构的函数并使用指向 AddVec 的指针类型参数。请检查我的答案和链接。
  • 现在你改变的是传递一个指向我编辑过的结构的指针,参数应该是指向Vector的指针。

标签: go struct gonum


【解决方案1】:

在 Golang 中,它被描述为提升的方法

Promoted 字段的行为类似于结构的普通字段,除了它们 不能用作结构的复合文字中的字段名称。

给定一个结构类型 S 和一个定义类型 T,提升的方法包含在结构的方法集中,如下所示:

  • 如果 S 包含嵌入字段 T,则 S 和 *S 的方法集都包括带有接收器 T 的提升方法。*S 的方法集还包括带有接收器 *T 的提升方法。
  • 如果 S 包含嵌入字段 *T,则 S 和 *S 的方法集都包含带有接收器 T 或 *T 的提升方法。

问题是您将指针类型参数传递给AddVec 函数。但是您在第二种情况下使用的是指针类型字段。

func (v *VecDense) AddVec(a, b Vector)

还有一点需要注意的是,AddVec 的值类型参数为 Vector 结构,但您将指向 Vector 字段的指针传递为

x, y := NewVector(3, []float64{1, 2, 3}), NewVector(3, []float64{4, 5, 6}) 

在上面的代码中x,y是从NewVector返回的指针类型

x.AddVec(x, y)

【讨论】:

  • 谢谢您的建议,但是如果类型不兼容,代码甚至无法编译吗? Vector 嵌入了VecDense 结构,所以我可以使用原始结构的方法。
  • @BalazsDianiska 尝试使用示例嵌入结构并检查您是否可以这样做。您可以通过调用父嵌入式结构的名称来实现。
  • 感谢您的提示,不幸的是问题仍然存在。
  • @BalazsDianiska 我注意到另一件事是NewVector 返回指针,而AddVec 的参数是值类型。
  • @BalazsDianiska 是的,这就是为什么我更改了答案并编辑了问题在于将参​​数传递给 AddVec 方法而不是接收器时。我知道嵌入式struct的方法被提示了。
【解决方案2】:

问题是在进行阴影检测时会比较指针。这是国际海事组织的一个错误。我刚刚提交了https://github.com/gonum/gonum/issues/945

【讨论】:

    猜你喜欢
    • 2012-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 2018-02-27
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    相关资源
    最近更新 更多