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.
Thanks a lot to jnml and ANisus for helping out !
With generics, you can declare and use functions or types that are written to work with any of a set of types provided by calling code. In this tutorial, you'll declare two simple non-generic functions, then capture the same logic in a single generic function.
Generics allows programmers to write general, generic code that works with different types and enables code reuse. Golang (Go), however, does not support generics, yet.
As we are all aware Go has no Generics, it was designed with simplicity in mind and Generics as mentioned above is considered to add complexity to the language. The same goes for Inheritance, Polymorphism and some other features that the top object-oriented languages showed when Go was created.
Downloadable at go. dev as of March 15, Go 1.18 introduces support for generic code using parameterized types. Generics has been called the most significant change to the Go programming language since the release of Go 1.0 in 2012. It was also the most-requested feature by Go programmers.
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 }))
}
Playground
Output:
true
false
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With