Go的头等function

我来自具有一streamfunction支持的JavaScript。 例如,您可以:

  • 将一个函数作为parameter passing给另一个函数
  • 从函数返回一个函数。

有人能给我一个例子,说明我会如何在Go上做这个事情吗?

去语言和function编程可能会有所帮助。 从这个博客文章:

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

作者是博客所有者:Dethe Elza(不是我)

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

输出:

 222111 222111 

规范中的相关部分: 函数types 。

所有其他的答案在这里首先声明一个新的types,这是好的(实践),使你的代码更容易阅读,但知道这不是一个要求。

您可以使用函数值,而无需为它们声明新types,如下面的示例所示。

声明一个具有2个float64types参数的函数types的variables,并且返回一个float64types的返回值,如下所示:

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

我们来写一个返回加法函数的函数。 这个加法器函数应该接受2个float64types的参数,并且在调用时返回这2个数字的和:

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

我们来编写一个函数,它有三个参数,前两个是float64types,第三个函数值是一个函数,它接受2个input参数,types为float64 ,产生一个float64types的值。 而且我们正在编写的函数将调用传递给它的函数值作为参数,并使用前2个float64值作为函数值的参数,并返回传递的函数值返回的结果:

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

让我们来看看我们之前的例子:

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

注意当然你可以在创buildadder时使用Shortvariables声明 :

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

在Go Playground上试试这些例子。

使用现有的function

当然,如果你已经有一个函数types的包中声明的函数,你也可以使用它。

例如math.Mod()具有相同的函数types:

 func Mod(x, y float64) float64 

所以你可以把这个值传递给我们的Execute()函数:

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

打印2因为12 mod 10 = 2 。 请注意,现有函数的名称充当函数值。

在Go Playground上试试吧。

注意:

请注意,参数名称不是types的一部分,具有相同参数和结果types的2个函数的types是相同的,而不pipe参数的名称如何。 但要知道,在参数或结果列表中,名称必须全部存在或全部不存在。

例如,你也可以写:

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

要么:

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

虽然你可以使用var或声明一个types,你不需要。 你可以很简单地做到这一点:

 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 

只是一个在Web应用程序中链接中间件的recursion函数定义的谜题。

首先,工具箱:

 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 ) 

正如你看到的typesChain是一个函数,返回相同typesChain另一个函数(第一类是怎么的!)。

现在进行一些testing,看看它在行动:

 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 

再次,这只是一个谜题,表明我们可以用不同的方式在Go中使用一stream的函数。 我个人现在用chi作为路由器和处理中间件。