在 Go 中,如何使用切片创建泛型函数?
In Go, how can I make a generic function with slices?
假设我想编写一个在切片中查找值的函数
直觉上我想写:
func find(s []interface{}, f func(interface{})bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
但是我无法用 Go 做到这一点。我可以有一个接口
Len() int
Value(int) interface{}
...
这会起作用,但在我的真实代码中,事情更复杂(我需要做切片[from:end]等),追加,...等等,如果我在一个界面中重新定义所有这些,我最终会有很多代码。有没有更好的方法?
如果您有像 []int
或 []string
这样的预定义类型并且不想转换为 []interface{}
请参阅此工作示例代码(不使用 reflect
):
package main
import "fmt"
func find(s []int, f func(int) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func findString(s []string, f func(string) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(s, func(a int) bool { return a == 5 })) //5
strs := []string{"A", "B", "C"}
fmt.Println(findString(strs, func(a string) bool { return a == "B" })) //1
}
或者您可以使用 reflect
,就像这个工作示例代码:
package main
import "fmt"
import "reflect"
func find(slice interface{}, f func(interface{}) bool) int {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice:
values := reflect.Indirect(reflect.ValueOf(slice))
for i := 0; i < values.Len(); i++ {
if f(values.Index(i).Interface()) {
return i
}
}
}
return -1
}
func main() {
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(a, func(i interface{}) bool { return i == 5 })) //5
b := []string{"A", "B", "C"}
fmt.Println(find(b, func(i interface{}) bool { return i == "B" })) //1
}
输出:
5
1
希望对您有所帮助。
你可以使用反射。我为一个项目写了这个函数,欢迎使用:
// InSlice returns true if value is in slice
func InSlice(value, slice interface{}) bool {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice, reflect.Ptr:
values := reflect.Indirect(reflect.ValueOf(slice))
if values.Len() == 0 {
return false
}
val := reflect.Indirect(reflect.ValueOf(value))
if val.Kind() != values.Index(0).Kind() {
return false
}
for i := 0; i < values.Len(); i++ {
if reflect.DeepEqual(values.Index(i).Interface(), val.Interface()) {
return true
}
}
}
return false
}
我认为,如果你想拥有任意值的切片并使用那种 find
函数并且有可能进行标准 []
重新切片,也许最好的方法是封装你的 interface{}
与另一个结构
type proxy struct {
val interface{}
}
并使用
func find(s []proxy , f func(proxy)bool) int {}
并让 f
函数处理 interface{}
比较/类型转换。
假设我想编写一个在切片中查找值的函数
直觉上我想写:
func find(s []interface{}, f func(interface{})bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
但是我无法用 Go 做到这一点。我可以有一个接口
Len() int
Value(int) interface{}
...
这会起作用,但在我的真实代码中,事情更复杂(我需要做切片[from:end]等),追加,...等等,如果我在一个界面中重新定义所有这些,我最终会有很多代码。有没有更好的方法?
如果您有像 []int
或 []string
这样的预定义类型并且不想转换为 []interface{}
请参阅此工作示例代码(不使用 reflect
):
package main
import "fmt"
func find(s []int, f func(int) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func findString(s []string, f func(string) bool) int {
for i, item := range s {
if f(item) {
return i
}
}
return -1
}
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(s, func(a int) bool { return a == 5 })) //5
strs := []string{"A", "B", "C"}
fmt.Println(findString(strs, func(a string) bool { return a == "B" })) //1
}
或者您可以使用 reflect
,就像这个工作示例代码:
package main
import "fmt"
import "reflect"
func find(slice interface{}, f func(interface{}) bool) int {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice:
values := reflect.Indirect(reflect.ValueOf(slice))
for i := 0; i < values.Len(); i++ {
if f(values.Index(i).Interface()) {
return i
}
}
}
return -1
}
func main() {
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(find(a, func(i interface{}) bool { return i == 5 })) //5
b := []string{"A", "B", "C"}
fmt.Println(find(b, func(i interface{}) bool { return i == "B" })) //1
}
输出:
5
1
希望对您有所帮助。
你可以使用反射。我为一个项目写了这个函数,欢迎使用:
// InSlice returns true if value is in slice
func InSlice(value, slice interface{}) bool {
switch reflect.TypeOf(slice).Kind() {
case reflect.Slice, reflect.Ptr:
values := reflect.Indirect(reflect.ValueOf(slice))
if values.Len() == 0 {
return false
}
val := reflect.Indirect(reflect.ValueOf(value))
if val.Kind() != values.Index(0).Kind() {
return false
}
for i := 0; i < values.Len(); i++ {
if reflect.DeepEqual(values.Index(i).Interface(), val.Interface()) {
return true
}
}
}
return false
}
我认为,如果你想拥有任意值的切片并使用那种 find
函数并且有可能进行标准 []
重新切片,也许最好的方法是封装你的 interface{}
与另一个结构
type proxy struct {
val interface{}
}
并使用
func find(s []proxy , f func(proxy)bool) int {}
并让 f
函数处理 interface{}
比较/类型转换。