我想要创建一个向量类型,它对其内部数据是通用的,但是在给定输入类型时,方法的实现方式可能有所不同。
type SupportedType interface {
~int64 | ~uint64 | ~float64 | string | bool | time.Time
}
type Vec[T SupportedType] struct {
data []T
}我想根据类型在函数上添加不同的实现。例如:
func (vec Vec[T]) Sort() {
...
}在大多数泛型类型中,<将很好地工作。但是,如果是T -> time.Time,我想使用Before方法,如果是T --> bool,那么我希望所有的假值都放在true之前。
我对如何做到这一点有一些想法,但是在新的仿制世界里,什么是“惯用的”呢?我的应用程序对性能敏感。
对所有函数都相同的类型使用类型联合不起作用(https://play.golang.com/p/QWE-XteWpjL)。
在类型特定的结构中嵌入容器确实有效( https://play.golang.com/p/j0AR48Mto-a ),但需要使用接口,这意味着示例函数中的Less和Val不能内联。如果类型联合中的子集之间没有清晰的描述,那么它也可能不能很好地工作。
发布于 2022-03-19 23:31:00
顺便说一下,已经有一个用于排序的库了
https://pkg.go.dev/golang.org/x/exp/slices#Sort
1.您可以使用泛型创建接口,然后键入assert。
示例:
type Lesser[T SupportedType] interface {
Less(T) bool
}
type Vec[T SupportedType] []T
func (vec Vec[T]) Less(a, b int) bool {
return any(vec[a]).(Lesser[T]).Less(vec[b])
}
func main() {
vs := Vec[String]([]String{"a", "b", "c", "d", "e"})
vb := Vec[Bool]([]Bool{false, true})
fmt.Println(vs.Less(3, 1))
fmt.Println(vb.Less(0, 1))
}2.您可以将类型保存在Vec上。
示例:
type Lesser[T SupportedType] interface {
Less(T) bool
}
type Vec[T SupportedType, L Lesser[T]] []T
func (vec Vec[T, L]) Less(a, b int) bool {
return any(vec[a]).(L).Less(vec[b])
}
func main() {
vs := Vec[String, String]([]String{"a", "b", "c", "d", "e"})
fmt.Println(vs.Less(3, 1))
}3.嵌套类型约束
谢谢@blackgreen
例子:
type SupportedType interface {
Int8 | Time | Bool | String
}
type Lesser[T SupportedType] interface {
Less(T) bool
}
type Vec[T interface {
SupportedType
Lesser[T]
}] []T
func (vec Vec[T]) Less(a, b int) bool {
return vec[a].Less(vec[b])
}
func main() {
vs := Vec[String]([]String{"a", "b", "c", "d", "e"})
fmt.Println(vs.Less(3, 1))
}基准:
benchmark 1 : 28093368 36.52 ns/op 16 B/op 1 allocs/op
benchmark 2 : 164784321 7.231 ns/op 0 B/op 0 allocs/op
benchmark 3 : 212480662 5.733 ns/op 0 B/op 0 allocs/op
Embedding a container inside type specific structs:
benchmark 4 : 211429621 5.720 ns/op 0 B/op 0 allocs/op这取决于你哪一个最适合你。但国际海事组织3号是最好的。
发布于 2022-03-20 09:31:53
就我个人而言,我认为最好不要在联合中包含许多彼此无关的类型,因为它们不会共享许多公共操作,并且您最终会编写特定于类型的代码。那么使用仿制药有什么意义.?
无论如何,可能的策略取决于SupportedType约束的类型集中包含了哪些内容,以及您希望如何处理这些约束:
只精确类型,不使用方法
在T上使用类型开关,并运行对具体类型有意义的任何操作。当方法实现只使用一个类型为T的值时,效果最好,因为您可以直接使用开关保护(v := any(vec[a]).(type))中的变量。当您在开关保护器中的值旁边有更多的T值时,它就不再漂亮了,因为您必须单独地转换和断言所有这些值:
func (vec Vec[T]) Less(a, b int) bool {
switch v := any(vec[a]).(type) {
case int64:
return v < any(vec[b]).(int64)
case time.Time:
return v.Before(any(vec[b]).(time.Time))
// more cases...
}
return false
}与方法
将包含方法的接口参数化,并将其T约束为受支持的类型。然后将Vector的类型参数限制在两个上,其优点是确保不能用忘记实现Less(T) bool的类型实例化Vector,并消除类型断言,否则,在运行时会出现恐慌。
type Lesser[T SupportedType] interface {
Less(T) bool
}
type Vec[T interface { SupportedType; Lesser[T] }] []T
func (vec Vec[T]) Less(a, b int) bool {
return vec[a].Less(vec[b])
}带有方法和预先声明类型的()
不可能。请考虑以下几点:
type SupportedTypes interface {
// exact predeclared types
int | string
}
type Lesser[T SupportedTypes] interface {
Less(T) bool
}约束Lesser有一个空类型集,因为int和string都不能有方法。在这里,您回到了“精确类型而没有方法”的情况。
具有近似类型(**~T**)的
将上述约束更改为近似类型:
type SupportedTypes interface {
// approximate types
~int | ~string
}
type Lesser[T SupportedTypes] interface {
Less(T) bool
}类型切换不是一个选项,因为case ~int:是不合法的。而约束上存在的方法阻止您使用预先声明的类型实例化:
Vector[MyInt8]{} // ok when MyInt8 implements Lesser
Vector[int8] // doesn't compile, int8 can't implement Lesser因此,我看到的选择是:
reflect.Kind或CanConvert进行一些黑客操作。当/如果这一提案通过时,这可能会改进,甚至可能超过其他选项。
https://stackoverflow.com/questions/71542373
复制相似问题