英文:
Idiomatic way to implement generic mappers in Go
问题
假设我想编写一个函数来检查切片中的元素是否满足谓词条件:
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; }) // 无法编译
类似地:
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; }) // 运行时发生错误(无法将[]int转换为[]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 (... 你拥有的任何迭代协议 ...) {
} else {
panic("无法迭代!")
}
return c
}
使用它重写我的初始示例在playground上。
英文:
Let's say I want to write a function to check whether a predicate is matched for an element in a slice:
func IsIn(array []T, pred func(elt T) bool) bool {
for _, obj := range array {
if pred(obj) { return true;}
}
return false;
}
Obviously, the previous code won't compile, since T
does not exist. I can replace it with some interface{}
like this:
func IsIn(array[]interface{}, pred func(elt interface{}) bool) bool {
...
}
As I am happy to let the predicate perform the casting:
IsIn([]interface{}{1,2,3,4}, func(o interface{}) {return o.(int) == 3; });
But then, the function won't accept any array which is not of type []interface{}
:
IsIn([]int{1,2,3,4}, func(o interface{}) { return o.(int) == 3; }) // DO NOT COMPILE
And similarly:
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)
The other alternative is to have typed functions for each array type:
IsInInt(arr []int, pred func(i int) bool) { ... }
IsInStr(arr []string, pred func(s string) bool) { ... }
...
But it seems like a LOT of code duplication.
Has anyone come up with an nice way to deal with such situations ?
EDIT
Thanks to jnml's fantastic tips on Go reflection, I think I have found a nice way to express these patterns, by converting every 'iterable' to a channel:
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;
}
With my initial example rewritten using it on the playground.
答案1
得分: 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 }))
}
输出:
true
false
英文:
For example:
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 }))
}
Output:
true
false
答案2
得分: 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()
函数。
你可以在这里找到一个可运行的示例。
英文:
I can't say if it is the most idiomatic. But one idiomatic solution would be to do like in the sort
package; to define an interface for the array:
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;
}
As long as your array implements this interface, you can use IsIn().
Working example can he found here
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论