Go 中的一流函数

发布于 2024-10-05 17:56:18 字数 138 浏览 11 评论 0原文

我来自 JavaScript,它具有一流的函数支持。例如,您可以:

  • 将一个函数作为参数传递给另一个函数
  • 从一个函数返回一个函数。

有人可以给我一个例子来说明如何在 Go 中做到这一点吗?

I come from JavaScript which has first class function support. For example you can:

  • pass a function as a parameter to another function
  • return a function from a function.

Can someone give me an example of how I would do this in Go?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

独自唱情﹋歌 2024-10-12 17:56:31

只是一个带有递归函数定义的脑筋急转弯,用于在 Web 应用程序中链接中间件。

首先,工具箱:

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
)

如您所见,Chain 类型是一个函数,它返回相同类型 Chain 的另一个函数(这真是一流!)。

现在进行一些测试来看看它的实际效果:

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 中使用第一类函数。就我个人而言,我现在使用 chi 作为路由器和处理中间件。

Just a brainteaser with recursive function definition for chaining middlewares in a web app.

First, the toolbox:

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
)

As you see type Chain is a function that returns another function of the same type Chain (How first class is that!).

Now some tests to see it in 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 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

Again, this was just a brainteaser to show we can use first class functions in Go in different ways. Personally I use chi nowadays as router and for handling middlewares.

帅的被狗咬 2024-10-12 17:56:27

虽然您可以使用 var 或声明类型,但您不需要这样做。
你可以非常简单地做到这一点:

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

While you can use a var or declare a type, you don't need to.
You can do this quite simply:

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
笔落惊风雨 2024-10-12 17:56:26

规范中的相关部分:函数类型

这里的所有其他答案首先声明一个新类型,这很好(实践)并且使您的代码更易于阅读,但要知道这不是必需的。

您可以使用函数值,而无需为其声明新类型,如下例所示。

声明一个函数类型的变量,该变量有 2 个 float64 类型的参数,并且有一个 float64 类型的返回值,如下所示:

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

让我们编写一个返回加法器函数的函数。此加法器函数应采用 2 个 float64 类型的参数,并在调用时返回这 2 个数字的总和:

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

让我们编写一个具有 3 个参数的函数,前 2 个为 float64 类型>,第三个是函数值,该函数采用 2 个 float64 类型的输入参数并生成 float64 类型的值。我们编写的函数将调用作为参数传递给它的函数值,并使用前 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

请注意,当然您可以使用 短变量声明 创建 adder 时:

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

Go Playground 上尝试这些示例。

使用现有函数

当然,如果您已经在包中声明了具有相同函数类型的函数,您也可以使用它。

例如 math.Mod() 具有相同的功能类型:

func Mod(x, y float64) float64

因此您可以将此值传递给我们的 Execute() 函数:

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

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

Go Playground 上尝试一下。

注意:

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

例如,您还可以写:

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

The related section from the specification: Function types.

All other answers here first declare a new type, which is good (practice) and makes your code easier to read, but know that this is not a requirement.

You can work with function values without declaring a new type for them, as seen in the below example.

Declaring a variable of function type which has 2 parameters of type float64 and has one return value of type float64 looks like this:

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

Let's write a function which returns an adder function. This adder function should take 2 parameters of type float64 and should returns the sum of those 2 numbers when called:

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

Let's write a function which has 3 parameters, first 2 being of type float64, and the 3rd being a function value, a function that takes 2 input parameters of type float64 and produces a value of float64 type. And the function we're writing will call the function value that is passed to it as parameter, and using the first 2 float64 values as arguments for the function value, and returns the result that the passed function value returns:

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

Let's see our previous examples in action:

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

Note that of course you can use the Short variable declaration when creating adder:

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

Try these examples on the Go Playground.

Using an existing function

Of course if you already have a function declared in a package with the same function type, you can use that too.

For example the math.Mod() has the same function type:

func Mod(x, y float64) float64

So you can pass this value to our Execute() function:

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

Prints 2 because 12 mod 10 = 2. Note that the name of an existing function acts as a function value.

Try it on the Go Playground.

Note:

Note that the parameter names are not part of the type, the type of 2 functions having the same parameter and result types is identical regardless of the names of the parameters. But know that within a list of parameters or results, the names must either all be present or all be absent.

So for example you can also write:

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

Or:

var adder func(x1, x2 float64) float64 = CreateAdder()
你没皮卡萌 2024-10-12 17:56:23

Go 语言和函数式编程可能会有所帮助。来自这篇博文:

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(不是我)

Go Language and Functional Programming might help. From this blog post:

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

Author is the blog owner: Dethe Elza (not me)

简单 2024-10-12 17:56:23
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
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)
}

output:

222111
222111
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文