英文:
Slice of (choose one out of finite number of interfaces)
问题
如何使RemoveDead
函数能够接受其他实现Liver
接口的接口切片(或者甚至是结构体指针切片),并且对性能影响较小?
我觉得这个函数需要接受一个interface{}
类型的参数,并进行运行时转换,但我不确定如何进行转换。我猜想使用x.(Liver)
比使用Liver(x)
要慢得多,因为后者是在编译时进行的转换。
最好的解决方案是复制粘贴该函数,并在每个副本中更改参数和返回类型吗?只需要三到四个副本,但这仍然感觉非常笨拙。
type Updater interface {
Liver
Update() bool
}
type Liver interface {
Alive() bool
}
func RemoveDead(all []Updater) []Updater {
for i := len(all) - 1; i >= 0; i-- {
if Liver(all[i]).Alive() == false {
all[i] = all[len(all)-1]
all = all[:len(all)-1]
}
}
return all
}
英文:
How can I make the RemoveDead
function accept other slices of interfaces (or maybe even slices of struct pointers) that implement Liver
with little impact on performance?
It seems to me that the function would have to take an interface{}
as argument and do runtime conversions, but I'm not sure how to do the conversions. I would also guess that doing x.(Liver)
is a lot slower than Liver(x)
, because the latter is a compile time conversion.
Is the best solution to copy-paste the function and change the argument and return type in each copy? Only three or four copies would be needed, but it would still feel like a very clumsy solution.
type Updater interface {
Liver
Update() bool
}
type Liver interface {
Alive() bool
}
func RemoveDead(all []Updater) []Updater {
for i := len(all) - 1; i >= 0; i-- {
if Liver(all[i]).Alive() == false {
all[i] = all[len(all)-1]
all = all[:len(all)-1]
}
}
return all
}
答案1
得分: 1
如您所提到的,无法通过简单的类型断言将类型为[]Updater
的切片转换为[]Liver
;它们的类型不是接口,而是接口的切片。出于同样的原因,无法将[]Updater
传递给需要[]interface{}
作为参数的函数。
然而,您可以使用reflect
包来实现您的需求。反射在某种程度上会影响性能。如果您认为性能开销太大,那么您可能需要使用复制粘贴的解决方案。
下面的代码可以改进,但它展示了如何使用反射解决该问题,并且在进行基准测试时可能会有用。当前,它将任何偶数的U值视为Alive:
package main
import (
"fmt"
"reflect"
)
type Updater interface {
Alive() bool
Update() bool
}
type Liver interface {
Alive() bool
}
type U int
func (u U) Alive() bool { return u%2 == 0 }
func RemoveDead(all interface{}) interface{} {
v := reflect.ValueOf(all)
if v.Kind() != reflect.Slice {
panic("RemoveDead requires a slice")
}
for i := v.Len() - 1; i >= 0; i-- {
l := v.Index(i)
if l.Interface().(Liver).Alive() == false {
l.Set(v.Index(v.Len() - 1))
v = v.Slice(0, v.Len()-1)
}
}
return v.Interface()
}
func main() {
u := []U{1, 4, 7, 2, 12}
fmt.Println("Before: ", u)
u = RemoveDead(u).([]U)
fmt.Println("After: ", u)
}
输出:
Before: [1 4 7 2 12]
After: [2 4 12]
英文:
As you mentioned, a slice of type []Updater
cannot be turned to a []Liver
with a simple type assertion; their type are not interfaces, but slices of interfaces. For the same reason is it not possible to pass a []Updater
to a function wanting an []interface{}
as parameter.
However, you can do what you desire using the reflect
package. Reflection is useful but will come at a cost on performance. If you consider the cost to high, then you will probably have to use the copy-paste solution.
The code below can surely be improved, but it shows how to solve the problem with reflection, and it might be useful when making a benchmark. Currently it regards any even U value as Alive:
package main
import (
"fmt"
"reflect"
)
type Updater interface {
Alive() bool
Update() bool
}
type Liver interface {
Alive() bool
}
type U int
func (u U) Alive() bool { return u % 2 == 0 }
func RemoveDead(all interface{}) interface{} {
v := reflect.ValueOf(all)
if v.Kind() != reflect.Slice {
panic("RemoveDead requires a slice")
}
for i := v.Len() - 1; i >= 0; i-- {
l := v.Index(i)
if l.Interface().(Liver).Alive() == false {
l.Set(v.Index(v.Len()-1))
v = v.Slice(0, v.Len()-1)
}
}
return v.Interface()
}
func main() {
u := []U{1,4,7,2,12}
fmt.Println("Before: ", u)
u = RemoveDead(u).([]U)
fmt.Println("After: ", u)
}
Output:
Before: [1 4 7 2 12]
After: [2 4 12]
答案2
得分: 0
你可以定义第三个接口:
type UpdaterLiver interface {
Updater
Liver
}
然后将RemoveDead
的定义更改为:
func RemoveDead(all []UpdaterLiver) []UpdaterLiver
英文:
You could define third interface:
type UpdaterLiver interface {
Updater
Liver
}
then change definition of RemoveDead
to be
func RemoveDead(all []UpdaterLiver) []UpdaterLiver
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论