2013-08-07 15 views
5

Powiedzmy chcę napisać funkcję, aby sprawdzić, czy orzeczenie jest dopasowany do elementu w plasterek:Idiomatic sposobem realizacji funkcji rodzajowe Przejdź

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

Oczywiście, poprzedni kod nie będzie kompilować, ponieważ T nie istnieje. Mogę wymienić go na jakiś interface{} tak:

func IsIn(array[]interface{}, pred func(elt interface{}) bool) bool { 
    ... 
} 

Ponieważ jestem szczęśliwy pozwolić orzeczenie wykonać rzucania:

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

ale wtedy funkcja nie ponosi żadnej tablicy, która nie jest od typu []interface{}:

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

I podobnie:

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) 

Drugą alternatywą jest wpisaniu funkcji dla każdego typu tablicy:

IsInInt(arr []int, pred func(i int) bool) { ... } 
IsInStr(arr []string, pred func(s string) bool) { ... } 
... 

Ale wydaje się, że dużo kodu powielania.

Czy ktoś ma dobry sposób radzenia sobie z takimi sytuacjami?

EDIT

Dzięki jnml „fantastycznych wskazówek s DALEJ refleksji, myślę, że znalazłem dobry sposób wyrażania tych wzorów, przekształcając każdy«iterable»do kanału:

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

Z moim początkowym przykładem przepisany przy użyciu go on the playground.

Wielkie dzięki za jnml i ANisus za pomoc!

Odpowiedz

4

na przykład:

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


wyjściowa:

true 
false 
+0

Nice! To załatwia sprawę. Dzięki za pokazanie mi tej części języka, której nie znałem :) – val

3

Nie mogę powiedzieć, czy jest to najbardziej idiomatyczny. Ale jednym idiomatycznym rozwiązaniem byłoby zrobienie czegoś w pakiecie sort; aby zdefiniować interfejs tablicy:

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

Dopóki Twoja tablica implementuje ten interfejs, możesz użyć IsIn().

przykład robocza może znalazł here