Я исхожу из JavaScript, который поддерживает функцию первого класса. Например, вы можете:
- передать функцию в качестве параметра другой функции
- возвращает функцию из функции.
Может кто-нибудь дать мне пример того, как я буду делать это в Go?
Я исхожу из JavaScript, который поддерживает функцию первого класса. Например, вы можете:
Может кто-нибудь дать мне пример того, как я буду делать это в Go?
Go Language и функциональное программирование могут помочь. Из этого сообщения в блоге:
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
Связанный раздел из спецификации: Типы функций.
Все остальные ответы здесь сначала объявляют новый тип, который хорош (практика) и делает ваш код более легким для чтения, но знайте, что это не является обязательным требованием.
Вы можете работать со значениями функций без объявления для них нового типа, как показано в приведенном ниже примере.
Объявление переменной типа функции, которая имеет 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
, а 3 - значение функции, функция, которая принимает 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.
Примечание:
Обратите внимание, что имена параметров не являются частью типа, тип 2 функций, имеющих одинаковый параметр и типы результатов, идентичен независимо от имен параметров. Но знайте, что в списке параметров или результатов имена должны либо присутствовать, либо вообще отсутствовать.
Так, например, вы также можете написать:
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 или declare a type, вам не нужно. Вы можете сделать это довольно просто:
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
Просто аналитик с рекурсивным определением функции для привязки middlewares в веб-приложении.
Во-первых, панель инструментов:
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 в настоящее время как маршрутизатор и для обработки посредников.