在Go中,如何使用切片创建泛型函数?

时间:2016-07-22 11:57:49

标签: go go-reflect

假设我想编写一个在切片中找到值的函数

我直觉地想写:

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]等),追加,等等,如果我在界面中重新定义所有这些,我最终会有一个很多代码。还有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

您可以使用反射。我为一个项目编写了这个函数,随意使用它:

// 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
}

答案 1 :(得分:1)

如果您有预定义的类型,例如[]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

我希望这会有所帮助。

答案 2 :(得分:0)

我认为,如果你想拥有任意值的片段并使用那种find函数并且有可能进行标准[]重复,那么最好的方法就是封装你的{{1与另一个结构

interface{}

并使用

type proxy struct {
    val interface{}
}

并让func find(s []proxy , f func(proxy)bool) int {} 函数处理f比较/类型转换。