英文:
Go (language) general numeric type/interface
问题
我正在尝试使用Go编写一个使用“通用”类型计算方程的包。具体来说,我想要实现龙格-库塔5阶逼近法。
这个逼近法通过仅使用t0
时刻的y
值、起始时间t0
、步长h
和形式为dy/dt = g(t,y)
的微分方程dgl
来计算在t0 + h
时刻的(未知)函数y
的值,其中g
是某个函数。
这个逼近法在使用标量类型和向量(甚至矩阵)时的行为完全相同。更一般地说:它适用于任何可以与相同类型的值相加/相减并可以通过标量进行缩放的东西(我使用float64
作为标量)。
因此,我尝试将其表示为Go接口:
type Numeric interface {
Add(rhs Numeric) Numeric
Sub(rhs Numeric) Numeric
Mul(rhs float64) Numeric
}
但是当我尝试“实现”这个接口时,由于参数类型的问题,我遇到了麻烦:
type Vec6F struct {
x, y, z float64
vx, vy, vz float64
}
func (lhs *Vec6F) Add(rhs *Vec6F) rk5.Numeric {
result := new(Vec6F)
result.x = lhs.x + rhs.x
result.y = lhs.y + rhs.y
result.z = lhs.z + rhs.z
result.vx = lhs.vx + rhs.vx
result.vy = lhs.vy + rhs.vy
result.vz = lhs.vz + rhs.vz
return result
}
这给我带来了错误:
cannot use result (type *Vec6F) as type rk5.Numeric in return argument:
*Vec6F does not implement rk5.Numeric (wrong type for Add method
have Add(*Vec6F) rk5.Numeric
want Add(rk5.Numeric) rk5.Numeric
这对我来说一方面是绝对合理的(因为rhs
可以是另一个实现了Numeric
接口的对象)。
但另一方面:我该如何在Go中表达这样的情况呢?在C++中,我可以使用运算符重载,但在Go中不可能。
英文:
I'm trying to write a package in Go that computes a equation using a "generic" type. To be specific, I want to implement the runge kutta 5 approximation.
This approximation calculates the value of a (unknown) function y
at the point t0 + h
using only the value of y
at t0
, the start time t0
, the step width h
and a differential equation dgl
which is of the form dy/dt = g(t,y)
where g
is some function.
This approximation behaves exactly the same when working with scalar types as when working with vectors (or even matrices). More generally speaking: It works with everything that can be added/subtracted to a value of the same type and can be scaled by a scalar (for which I use float64
)
So I tried to express this as a Go interface:
type Numeric interface {
Add(rhs Numeric) Numeric
Sub(rhs Numeric) Numeric
Mul(rhs float64) Numeric
}
But when I try to "implement" this interface, I ran into troubles because of the parameters type:
type Vec6F struct {
x, y, z float64
vx, vy, vz float64
}
func (lhs *Vec6F) Add(rhs *Vec6F) rk5.Numeric {
result := new(Vec6F)
result.x = lhs.x + rhs.x
result.y = lhs.y + rhs.y
result.z = lhs.z + rhs.z
result.vx = lhs.vx + rhs.vx
result.vy = lhs.vy + rhs.vy
result.vz = lhs.vz + rhs.vz
return result
}
This gives me the error
cannot use result (type *Vec6F) as type rk5.Numeric in return argument:
*Vec6F does not implement rk5.Numeric (wrong type for Add method
have Add(*Vec6F) rk5.Numeric
want Add(rk5.Numeric) rk5.Numeric
which is, on the one hand absolutely logic to me (because rhs could be another object implementing Numeric)
But on the other hand: How do I express something like that in Go? In C++ I could use operator overloading instead, but thats not possible in go.
答案1
得分: 2
为了使你的Add
方法具有通用性,它必须接受一个Numeric
参数。处理这个问题的常规方法是使用类型断言,如下所示(在playground上查看)
func (lhs *Vec6F) Add(_rhs Numeric) Numeric {
result := new(Vec6F)
rhs := _rhs.(*Vec6F) // 类型断言 - 如果传入的类型不正确,将会引发panic
result.x = lhs.x + rhs.x
result.y = lhs.y + rhs.y
result.z = lhs.z + rhs.z
result.vx = lhs.vx + rhs.vx
result.vy = lhs.vy + rhs.vy
result.vz = lhs.vz + rhs.vz
return result
}
如果你有不同的类型需要进行转换,你也可以使用类型切换。
英文:
In order to be generic your Add
method must take a Numeric
parameter. The normal way to deal with this is with a type assertion like this (on playground)
func (lhs *Vec6F) Add(_rhs Numeric) Numeric {
result := new(Vec6F)
rhs := _rhs.(*Vec6F) // type assertion - will panic if wrong type passes
result.x = lhs.x + rhs.x
result.y = lhs.y + rhs.y
result.z = lhs.z + rhs.z
result.vx = lhs.vx + rhs.vx
result.vy = lhs.vy + rhs.vy
result.vz = lhs.vz + rhs.vz
return result
}
You could also use a type switch if you had different types you wanted to convert between.
答案2
得分: 1
确实,在Go语言中不支持泛型。
如果你想让一个类型实现一个接口,方法的原型需要完全匹配:你需要 func (lhs *Vec6F) Add(rhs Numeric) Numeric
。
下面是使用类型断言编写此方法的尝试:
func (lhs *Vec6F) Add(rhs Numeric) Numeric {
vrhs := rhs.(*Vec6F)
result := new(Vec6F)
result.x = lhs.x + vrhs.x
result.y = lhs.y + vrhs.y
result.z = lhs.z + vrhs.z
result.vx = lhs.vx + vrhs.vx
result.vy = lhs.vy + vrhs.vy
result.vz = lhs.vz + vrhs.vz
return result
}
它可以编译,并且在使用正确类型的参数调用时应该可以工作,但是我认为这是一种滥用。
除非运行时错误,否则没有任何限制阻止你将此方法用于将向量添加到标量,因为它们都实现了 Numeric
接口。最终,使用接口抽象并不会带来任何好处。
在这种情况下,Go语言的哲学会建议使用特定类型的方法/函数。
英文:
Indeed, generics are not supported in go.
If you want a type to implement an interface, the methods' prototypes need to match exactly: you would need func (lhs *Vec6F) Add(rhs Numeric) Numeric
.
Here is an attempt to write this method using a type assertion:
func (lhs *Vec6F) Add(rhs Numeric) Numeric {
vrhs := rhs.(*Vec6F)
result := new(Vec6F)
result.x = lhs.x + vrhs.x
result.y = lhs.y + vrhs.y
result.z = lhs.z + vrhs.z
result.vx = lhs.vx + vrhs.vx
result.vy = lhs.vy + vrhs.vy
result.vz = lhs.vz + vrhs.vz
return result
}
It compiles and should work when called with the right types of argument, however, I'd say it's an abuse.
Nothing stops you (except a runtime error) from using this method to add vectors to scalars since they would both implement Numeric
. In the end, you would gain nothing from using the interface abstraction.
The go philosophy would dictate the use of type-specific methods/functions in this case.
答案3
得分: 0
你遇到了两个问题。
1.) 它无法编译,并且抱怨接口不匹配的原因是 Vec6F 不满足 rk5.Numeric 的函数签名。返回值和输入参数都必须匹配类型。
http://play.golang.org/p/kc9V9EXxJq 修复了这个问题,但又引入了一个新问题...
2.) 为了使方法签名匹配,使 Vec6F 满足 Numeric 的签名,它破坏了对属性值执行数值操作的能力。这是因为接口只有方法,没有属性。
在你的用例中,Numeric 接口是否提供一个访问器方法,该方法返回一个矩阵数组,接收器将在其上执行 Add|Sub|Multi 操作,这样做可能会使每个接口实现的方法变得复杂,但我认为这会得到你想要的结果。
英文:
There are two problems that you're running into.
1.) The reason it does not compile, and complains about the interfaces not matching is because Vec6F does not satisfy the function signature for rk5.Numeric. Both return value, and input parameters must match type.
http://play.golang.org/p/kc9V9EXxJq fixes that problem but creates a new one...
2.) To make the method signatures match so Vec6F satisfies Numeric's signature it broke the ability to perform numeric operations on the property values. This is because interfaces have only methods, no properties.
In your usecase, would it make sense for the Numeric interface to provide an accessor method which would return a matrix array that the receiver would then perform the Add|Sub|Multi on? This might complicate what needs to be done within each interface implementation's methods but I think would get you what you're looking for.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论