私は、ファーストクラスの機能をサポートするJavaScriptから来ました。たとえば、次のことができます。
誰かがGoでこれを行う方法の例を教えてもらえますか?
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(私ではありません)
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
仕様の関連セクション: Function types 。
ここでの他のすべての答えは、最初に新しいタイプを宣言します。これは適切で(練習)、コードを読みやすくしますが、これは要件ではないことを知っています。
次の例に示すように、新しい値を宣言せずに関数値を操作できます。
タイプ_float64
_の2つのパラメーターと、タイプ_float64
_の1つの戻り値を持つ関数タイプの変数の宣言は次のようになります。
_// Create a var of the mentioned function type:
var f func(float64, float64) float64
_
加算関数を返す関数を書きましょう。この加算関数は、_float64
_型の2つのパラメーターを受け取り、呼び出されたときにこれらの2つの数値の合計を返す必要があります。
_func CreateAdder() func(float64, float64) float64 {
return func(x, y float64) float64 {
return x + y
}
}
_
最初の2つが_float64
_型であり、3番目が関数値である3つのパラメーターを持つ関数を作成しましょう。関数は、タイプ_float64
_の2つの入力パラメーターを取り、_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
を作成するときに Short変数宣言 を使用できることに注意してください。
_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を使用したり、型を宣言したりできますが、必要はありません。これは非常に簡単に行えます:
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アプリでミドルウェアを連鎖するための再帰的な関数定義を備えた単なる頭の体操。
まず、ツールボックス:
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 をルーターとして、またミドルウェアの処理に使用しています。