界面的切片(从有限数量的界面中选择一个)

huangapple go评论88阅读模式
英文:

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]

Playground

英文:

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]

Playground

答案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

huangapple
  • 本文由 发表于 2013年12月6日 20:50:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/20424379.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定