Функции первого класса в Go - PullRequest
51 голосов
/ 05 декабря 2010

Я пришел из JavaScript, который имеет поддержку функций первого класса. Например, вы можете:

  • передать функцию в качестве параметра другой функции
  • возвращает функцию из функции.

Может ли кто-нибудь дать мне пример того, как я мог бы сделать это в Go?

Ответы [ 5 ]

41 голосов
/ 05 декабря 2010

Язык программирования 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 (не я)

27 голосов
/ 28 июня 2013
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
6 голосов
/ 31 июля 2015

Соответствующий раздел из спецификации: Типы функций .

Все остальные ответы здесь сначала объявляют новый тип, который является хорошим (практическим) и облегчает чтение вашего кода, но знайте, что это не является обязательным требованием.

Вы можете работать со значениями функций, не объявляя для них новый тип, как показано в следующем примере.

Объявление переменной типа функции, которая имеет 2 параметра типа float64 и имеет одно возвращаемое значение типа float64, выглядит следующим образом:

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

Давайте напишем функцию, которая возвращает функцию сумматора. Эта функция сумматора должна принимать 2 параметра типа float64 и возвращать сумму этих двух чисел при вызове:

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()
1 голос
/ 11 июня 2016

Хотя вы можете использовать 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
0 голосов
/ 21 февраля 2017

Просто головоломка с рекурсивным определением функции для связывания промежуточного программного обеспечения в веб-приложении.

Во-первых, набор инструментов:

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 в настоящее время в качестве маршрутизатора и для работы с промежуточным программным обеспечением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...