tipos programacion primera functions funciones funcional first especificas clase acciones go functional-programming

programacion - Funciones de primera clase en Go



programacion funcional (5)

Vengo de JavaScript, que tiene soporte para funciones de primera clase. Por ejemplo, puedes:

  • pasar una función como parámetro a otra función
  • devolver una función desde una función.

¿Puede alguien darme un ejemplo de cómo haría esto en Go?


La sección relacionada de la especificación: Tipos de funciones .

Todas las otras respuestas aquí primero declaran un nuevo tipo, que es bueno (práctica) y hace que su código sea más fácil de leer, pero saben que esto no es un requisito.

Puede trabajar con valores de función sin declarar un nuevo tipo para ellos, como se ve en el ejemplo siguiente.

Declarar una variable de tipo de función que tiene 2 parámetros de tipo float64 y tiene un valor de retorno de tipo float64 ve así:

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

Escribamos una función que devuelve una función sumadora. Esta función sumadora debe tomar 2 parámetros de tipo float64 y debe devolver la suma de esos 2 números cuando se le llame:

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

Escribamos una función que tiene 3 parámetros, los primeros 2 son de tipo float64 , y el 3 es un valor de función, una función que toma 2 parámetros de entrada de tipo float64 y produce un valor de tipo float64 . Y la función que estamos escribiendo llamará al valor de la función que se le pasa como parámetro, y utilizando los primeros 2 valores de float64 como argumentos para el valor de la función, y devuelve el resultado que devuelve el valor de la función aprobada:

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

Veamos nuestros ejemplos anteriores en acción:

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

Tenga en cuenta que, por supuesto, puede usar la declaración de variable corta al crear un adder :

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

Pruebe estos ejemplos en el Go Playground .

Usando una función existente

Por supuesto, si ya tiene una función declarada en un paquete con el mismo tipo de función, puede usarla también.

Por ejemplo, math.Mod() tiene el mismo tipo de función:

func Mod(x, y float64) float64

Entonces puede pasar este valor a nuestra función Execute() :

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

Imprime 2 porque 12 mod 10 = 2 . Tenga en cuenta que el nombre de una función existente actúa como un valor de función.

Pruébalo en el patio de juegos Go .

Nota:

Tenga en cuenta que los nombres de los parámetros no son parte del tipo, el tipo de 2 funciones que tienen el mismo parámetro y los mismos tipos de resultados son idénticos independientemente de los nombres de los parámetros. Pero sepa que dentro de una lista de parámetros o resultados, los nombres deben estar todos o estar ausentes.

Entonces, por ejemplo, también puedes escribir:

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

O:

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


Si bien puede usar una var o declarar un tipo, no es necesario. Puedes hacer esto simplemente:

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


Solo un rompecabezas con definición de función recursiva para encadenar middlewares en una aplicación web.

Primero, la caja de herramientas:

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 )

Como ve, escriba Chain es una función que devuelve otra función del mismo tipo Chain (¿Cómo es la primera clase?).

Ahora algunas pruebas para verlo en acción:

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 string) 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 string) 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 string

Una vez más, esto fue solo un acertijo para mostrar que podemos usar funciones de primera clase en Go de diferentes maneras. Personalmente uso chi hoy en día como enrutador y para manejar middlewares.


El lenguaje Go y la programación funcional pueden ayudar. De esta publicación en el blog:

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

El autor es el propietario del blog: Dethe Elza (no yo)


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

salida:

222111 222111