2013-08-02 11 views

Odpowiedz

31

Funkcje Go to wartości pierwszej klasy. Nie musisz powracać do słabych sztuczek z dynamicznych języków.

package main 

import "fmt" 

func someFunction1(a, b int) int { 
     return a + b 
} 

func someFunction2(a, b int) int { 
     return a - b 
} 

func someOtherFunction(a, b int, f func(int, int) int) int { 
     return f(a, b) 
} 

func main() { 
     fmt.Println(someOtherFunction(111, 12, someFunction1)) 
     fmt.Println(someOtherFunction(111, 12, someFunction2)) 
} 

Playground


wyjściowa:

123 
99 

Jeżeli wybór funkcji zależy na jakimś okresie czasu tylko znanej wartości, można użyć mapę:

m := map[string]func(int, int) int { 
     "someFunction1": someFunction1, 
     "someFunction2": someFunction2, 
} 

... 

z := someOtherFunction(x, y, m[key]) 
+0

Dzięki! Jest to powszechnie znana i fajna własność językowa. Ale nie jest to dokładnie to, co chcę robić. Pozwól mi wyjaśnić szerzej: Mam pakiet (A) z kilkoma funkcjami i mam plaster z ciągów w innym pakiecie (B) - nazwy tych funkcji. I muszę podać nazwę funkcji z pakietu A do innej metody z pakietu B. I po prostu nie mogę używać ciągów jako argumentów, potrzebuję dokładnie nazwy funkcji. Dziwny łuk, tak. Ale myślę, że Go może sobie z tym poradzić :) –

+0

@SergeyGerasimov: Jeśli chcesz przekazać funkcję po nazwie (_ "Potrzebuję dokładnie nazwy funkcji" _), użyj identyfikatora funkcji. Jeśli chcesz przekazać go jako ciąg ('" foo "'), przekaż identyfikator funkcji w cudzysłowach. Obie techniki pokazano powyżej. A może nie rozumiem, co masz na myśli? – zzzz

+2

Myślę, że on oznacza, że ​​musi, biorąc pod uwagę łańcuchową reprezentację nazwy funkcji, uzyskać (i prawdopodobnie ostatecznie wywołać) wskaźnik do nazwanej funkcji. Zakładam, że będziesz musiał użyć pakietu reflect w jakiś sposób. – joshlf

3

Jestem n całkowicie jasne, co chcesz zrobić. Odpowiedź jnml powinna obejmować podstawy wszystkiego, co próbujesz zrobić.

Chciałbym dodać, że pakiet crypto ma interesujący sposób pośredniego rejestrowania innych pakietów. W szczególności spójrz na crypto.go.

Zasadniczo, jak to działa to pakiet crypto ma pustą mapę takiego:

var regFuncs = make(map[key]func (arg) result) 

Gdzie „klucz” będzie unikatowy typ (int, string, etc ..), a wartość będzie prototyp funkcji, którego się spodziewasz.

Opakowanie to wówczas rejestruje się za pomocą funkcji init:

func init() { 
    master.RegisterFunc("name", myFunc) 
} 

pakiet ten sam być uwzględnione przy użyciu import _ "path/to/package".

A następnie pakiet główny będzie miał sposób na pobranie tej funkcji.

Z crypto, można użyć sha 256 tak:

crypto.SHA256.New() 

Ale trzeba najpierw umieścić go w głównym tak:

import _ "crypto/sha256" 

Mam nadzieję, że to pomaga.

+0

Wow, dzięki! W rzeczywistości rozumiem, że sztuczki z dynamicznych języków nie działają w Go i zdecydowanie powinienem przemyśleć architekturę tego problemu. W każdym razie, dziękuję bardzo! –

10

@ Odpowiedź jnml jest prawdopodobnie tym, co powinieneś zrobić.

Oto podejście z wykorzystaniem refleksji, która pozwala również przekazywać elastyczną liczbę argumentów. obecnie konieczne jest ręczne utworzenie listy obsługiwanych funkcji (mapy) (patrz metoda główna), ale może to zostać poprawione przez kogoś.

package main 

import "fmt" 
import "reflect" 
import "errors" 

func foo() { 
    fmt.Println("we are running foo") 
} 

func bar(a, b, c int) { 
    fmt.Println("we are running bar", a, b, c) 
} 

func Call(m map[string]interface{}, name string, params ... interface{}) (result []reflect.Value, err error) { 
    f := reflect.ValueOf(m[name]) 
    if len(params) != f.Type().NumIn() { 
     err = errors.New("The number of params is not adapted.") 
     return 
    } 
    in := make([]reflect.Value, len(params)) 
    for k, param := range params { 
     in[k] = reflect.ValueOf(param) 
    } 
    result = f.Call(in) 
    return 
} 

func main() { 
    // nota bene: for perfect score: use reflection to build this map 
    funcs := map[string]interface{} { 
      "foo": foo, 
      "bar": bar, 
    } 

    Call(funcs, "foo") 
    Call(funcs, "bar", 1, 2, 3) 
} 

inspiracji/źródło: http://www.mikespook.com/2012/07/function-call-by-name-in-golang/

+0

Bardzo podoba mi się ta odpowiedź, była bardzo pomocna w projekcie, nad którym pracuję. Mam jedno pytanie, w jaki sposób uzyskać wartość zwracaną z funkcji uruchamianej w ten sposób? Jedyny zwrot, jaki otrzymuję, to typ zwracany, a nie rzeczywista wartość. –

+0

Wydaje się, że odpowiedź jnml zniknęła. – converter42

+0

jnml zmienił nazwę na zzzz – mnagel

Powiązane problemy