Fonctions de première classe dans Go

Je viens de JavaScript qui prend en charge la fonction de première classe. Par exemple, vous pouvez:

  • passer une fonction comme paramètre à une autre fonction
  • renvoie une fonction d’une fonction.

Est-ce que quelqu’un peut me donner un exemple de comment je ferais ceci dans Go?

Go Language et la functional programming peuvent aider. De cet article de blog:

package main import fmt "fmt" type Ssortingngy func() ssortingng func foo() ssortingng{ return "Ssortingngy function" } func takesAFunction(foo Ssortingngy){ fmt.Printf("takesAFunction: %v\n", foo()) } func returnsAFunction()Ssortingngy{ return func()ssortingng{ fmt.Printf("Inner ssortingngy function\n"); return "bar" // have to return a ssortingng to be ssortingngy } } func main(){ takesAFunction(foo); var f Ssortingngy = returnsAFunction(); f(); var baz Ssortingngy = func()ssortingng{ return "anonymous ssortingngy\n" }; fmt.Printf(baz()); } 

L’auteur est le propriétaire du blog: Dethe Elza (pas moi)

 package main import ( "fmt" ) type Lx func(int) int func cmb(f, g Lx) Lx { return func(x int) int { return g(f(x)) } } func inc(x int) int { return x + 1 } func sum(x int) int { result := 0 for i := 0; i < x; i++ { result += i } return result } func main() { n := 666 fmt.Println(cmb(inc, sum)(n)) fmt.Println(n * (n + 1) / 2) } 

sortie:

 222111 222111 

La section associée de la spécification: Types de fonctions .

Toutes les autres réponses ici déclarent d’abord un nouveau type, ce qui est bien (pratique) et rend votre code plus facile à lire, mais sachez que ce n’est pas une exigence.

Vous pouvez travailler avec des valeurs de fonction sans en déclarer un nouveau, comme dans l’exemple ci-dessous.

Déclarer une variable de type fonction ayant 2 parameters de type float64 et ayant une valeur de retour de type float64 :

 // Create a var of the mentioned function type: var f func(float64, float64) float64 

Écrivons une fonction qui renvoie une fonction d’additionneur. Cette fonction d’addition devrait prendre 2 parameters de type float64 et devrait retourner la sum de ces 2 nombres lorsqu’elle est appelée:

 func CreateAdder() func(float64, float64) float64 { return func(x, y float64) float64 { return x + y } } 

float64 une fonction qui a 3 parameters, le premier 2 étant de type float64 et le 3ème étant une valeur de fonction, une fonction qui prend 2 parameters d’entrée de type float64 et produit une valeur de type float64 . Et la fonction que nous écrivons appelle la valeur de la fonction qui lui est transmise en tant que paramètre, et utilise les 2 float64 valeurs float64 comme arguments pour la valeur de la fonction, et renvoie le résultat que la valeur de la fonction passée renvoie:

 func Execute(a, b float64, op func(float64, float64) float64) float64 { return op(a, b) } 

Voyons nos exemples précédents en action:

 var adder func(float64, float64) float64 = CreateAdder() result := Execute(1.5, 2.5, adder) fmt.Println(result) // Prints 4 

Notez bien entendu que vous pouvez utiliser la déclaration de variable Short lors de la création d’un adder :

 adder := CreateAdder() // adder is of type: func(float64, float64) float64 

Essayez ces exemples sur le terrain de jeu Go .

Utiliser une fonction existante

Bien sûr, si vous avez déjà une fonction déclarée dans un package avec le même type de fonction, vous pouvez l’utiliser également.

Par exemple, le math.Mod() a le même type de fonction:

 func Mod(x, y float64) float64 

Vous pouvez donc transmettre cette valeur à notre fonction Execute() :

 fmt.Println(Execute(12, 10, math.Mod)) // Prints 2 

Imprime 2 car 12 mod 10 = 2 . Notez que le nom d’une fonction existante agit comme une valeur de fonction.

Essayez-le sur le terrain de jeu Go .

Remarque:

Notez que les noms de parameters ne font pas partie du type, le type de 2 fonctions ayant les mêmes parameters et types de résultats est identique, quels que soient les noms des parameters. Mais sachez que dans une liste de parameters ou de résultats, les noms doivent tous être présents ou absents.

Ainsi, par exemple, vous pouvez également écrire:

 func CreateAdder() func(P float64, Q float64) float64 { return func(x, y float64) float64 { return x + y } } 

Ou:

 var adder func(x1, x2 float64) float64 = CreateAdder() 

Bien que vous puissiez utiliser un type var ou declare un type, vous n’avez pas besoin de le faire. Vous pouvez le faire tout simplement:

 package main import "fmt" var count int func increment(i int) int { return i + 1 } func decrement(i int) int { return i - 1 } func execute(f func(int) int) int { return f(count) } func main() { count = 2 count = execute(increment) fmt.Println(count) count = execute(decrement) fmt.Println(count) } //The output is: 3 2 

Juste un casse-tête avec définition de fonction récursive pour chaîner les middlewares dans une application Web.

Tout d’abord, la boîte à outils:

 func MakeChain() (Chain, http.Handler) { nop := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {}) var list []Middleware var final http.Handler = nop var f Chain f = func(m Middleware) Chain { if m != nil { list = append(list, m) } else { for i := len(list) - 1; i >= 0; i-- { mid := list[i] if mid == nil { continue } if next := mid(final); next != nil { final = next } else { final = nop } } if final == nil { final = nop } return nil } return f } return f, final } type ( Middleware func(http.Handler) http.Handler Chain func(Middleware) Chain ) 

Comme vous le voyez, la Chain est une fonction qui renvoie une autre fonction du même type.

Maintenant, quelques tests pour le voir en action:

 func TestDummy(t *testing.T) { c, final := MakeChain() c(mw1(`OK!`))(mw2(t, `OK!`))(nil) log.Println(final) w1 := httptest.NewRecorder() r1, err := http.NewRequest("GET", "/api/v1", nil) if err != nil { t.Fatal(err) } final.ServeHTTP(w1, r1) } func mw2(t *testing.T, expectedState ssortingng) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { val := r.Context().Value(contextKey("state")) sval := fmt.Sprintf("%v", val) assert.Equal(t, sval, expectedState) }) } } func mw1(initialState ssortingng) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), contextKey("state"), initialState) next.ServeHTTP(w, r.WithContext(ctx)) }) } } type contextKey ssortingng 

Encore une fois, ce n’était qu’un casse-tête pour montrer que nous pouvons utiliser des fonctions de première classe dans Go de différentes manières. Personnellement, j’utilise chi aujourd’hui comme routeur et pour gérer les middlewares.