在Go中实现通用映射器的惯用方法

5

假设我想编写一个函数来检查切片中的元素是否匹配谓词:

func IsIn(array []T, pred func(elt T) bool) bool {
    for _, obj := range array {
        if pred(obj) { return true;}
    }
    return false;
}

显然,以前的代码不会编译,因为“T”不存在。我可以用一些“interface {}”来替换它,就像这样:
func IsIn(array[]interface{}, pred func(elt interface{}) bool) bool {
    ...
}

我很乐意让谓词执行转换:

IsIn([]interface{}{1,2,3,4}, func(o interface{}) {return o.(int) == 3; });

但是,该函数不接受任何不属于类型[]interface{}的数组:

IsIn([]int{1,2,3,4}, func(o interface{}) { return o.(int) == 3; }) // DO NOT COMPILE

同样地,还有:
func IsIn(arr interface, pred func(o interface{}) bool) bool {
    for _, o := range arr.([]interface{}) { ... }
}
IsIn([]int{1,2,3,4}, func(o interface{}) { return o.(int) == 3; }) // PANICS AT RUNTIME (cannot cast []int to []interface)

另一种选择是针对每种数组类型都有一个已键入的函数:
IsInInt(arr []int, pred func(i int) bool) { ... }
IsInStr(arr []string, pred func(s string) bool) { ... }
...

但是这似乎存在着很多代码重复的问题。

有没有人想出了一个好的方法来处理这种情况呢?

编辑

感谢jnml关于Go反射的绝妙提示,我认为通过将每个“可迭代”转换为通道的方式,我已经找到了一种不错的表达这些模式的方法:

func iter(obj interface{}) chan interface{} {
    c := make(chan interface{})
    v := reflect.ValueOf(obj)
    if (v.Kind() == reflect.Array || v.Kind() == reflect.Slice) {
        go func() {
            for i := 0; i < v.Len(); i++ {
                c<-v.Index(i).Interface()
            }
            close(c)
        }()
    } else if v.Kind() == reflect.Chan {
        go func() {
            x, ok := v.Recv()
            for ok {
                c<-x.Interface()
                x,ok = v.Recv()
            }
            close(c)
        }()
    } else if (... whatever iteration protocol you have ...) {
    } else {
        panic("Cannot iterate !")
    }
    return c;
}

使用Playground重新编写了我的初始示例。

非常感谢jnmlANisus的帮助!


一个旧问题的新答案... https://go2goplay.golang.org/p/pjvmdXJn7uL - jub0bs
2个回答

4

例如:

package main

import (
        "fmt"
        "reflect"
)

func checkSlice(slice interface{}, predicate func(reflect.Value) bool) bool {
        v := reflect.ValueOf(slice)
        if v.Kind() != reflect.Slice {
                panic("not a slice")
        }

        for i := 0; i < v.Len(); i++ {
                if predicate(v.Index(i)) {
                        return true
                }
        }

        return false
}

func main() {
        a := []int{1, 2, 3, 4, 42, 278, 314}
        fmt.Println(checkSlice(a, func(v reflect.Value) bool { return v.Int() == 42 }))

        b := []float64{1.2, 3.4, -2.5}
        fmt.Println(checkSlice(b, func(v reflect.Value) bool { return v.Float() > 4 }))
}

Playground


输出:

true
false

很好!这就是解决问题的方法。 谢谢你向我展示了我不知道的语言部分 :) - val

3

我不能确定它是否是最惯用的方法,但是一个惯用的解决方案是像sort包中一样,为数组定义一个接口:

type Interface interface {
    Len() int
    Equal(i int, v interface{}) bool
}

func IsIn(array Interface, value interface{}) bool {
    for i := 0; i < array.Len(); i++ {
    if array.Equal(i, value) {
            return true
        }
    }
    return false;
}

只要您的数组实现了这个接口,就可以使用IsIn()方法。
可以在这里找到工作示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接