Go 言語における匿名関数

GolangGolangBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

前の実験では、名前付き関数の書き方と使い方、コードをモジュールに整理する方法、および論理を個別の関数に分割することでコードの読みやすさを向上させる方法を学びました。この実験では、名前のない特別な関数タイプである匿名関数を探ります。匿名関数は、個別の名前付き関数を宣言することなく、小さな論理を「その場で」定義したい場合に便利です。特に短く独立した操作、または関数を別の関数に引数として渡す必要がある場合(コールバックのような場合)に役立ちます。匿名関数を使うことで、より簡潔で表現力のあるコードを書くことができます。

主なトピック

  • 匿名関数とは何か、そしてそれを定義する方法
  • 匿名関数を使う理由と時機
  • 名前に割り当てることなく匿名関数を呼び出す方法
  • 匿名関数にパラメータを渡して値を返す方法
  • より柔軟なコードのために匿名関数をコールバック関数として使う方法

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("Golang")) -.-> go/BasicsGroup(["Basics"]) go(("Golang")) -.-> go/FunctionsandControlFlowGroup(["Functions and Control Flow"]) go/BasicsGroup -.-> go/values("Values") go/BasicsGroup -.-> go/variables("Variables") go/FunctionsandControlFlowGroup -.-> go/functions("Functions") go/FunctionsandControlFlowGroup -.-> go/closures("Closures") subgraph Lab Skills go/values -.-> lab-149099{{"Go 言語における匿名関数"}} go/variables -.-> lab-149099{{"Go 言語における匿名関数"}} go/functions -.-> lab-149099{{"Go 言語における匿名関数"}} go/closures -.-> lab-149099{{"Go 言語における匿名関数"}} end

匿名関数の理解

Go言語における匿名関数は、通常の関数と同じように定義されますが、名前がありません。代わりに、変数に割り当てることができ、引数として渡すことができ、または定義の直後に即座に実行することができます。これにより、短い一回限りの操作や、関数を他の関数に引数として渡す場合に適しています。匿名関数は、一度だけ必要な関数で、個別の名前付き関数を必要としない場合に特に役立ちます。論理を使用場所に近づけることで、コードの読みやすさを向上させることができます。

匿名関数の構文

func(input parameters)(return parameters) {
    // コードブロック
}

これは、関数名がないことを除けば、通常の関数を定義するのと似ています。

通常の関数宣言と比較すると:

// 通常の関数宣言
func functionName(parameters...)(return values...) {
    コードブロック
}

なぜ匿名関数を使うのか?

  • 簡潔性:個別の名前付き関数を作成することなく、小さな論理を定義できるため、コードがより簡潔になります。
  • ローカルスコープ:匿名関数のスコープは、囲まれた関数内にあるため、名前空間の汚染を制限します。
  • 柔軟性:他の関数に引数として渡すことができ、または定義して直ちに実行することができます。

匿名関数を使う時機?

  • 他の場所で再利用されない短い関数が必要な場合。
  • コールバック関数として(後で見ます)。
  • 関数を直ちに実行したい場合(多くの場合初期化のため)。

パラメータのない匿名関数の作成

まずは、匿名関数を使って "hello world" を出力する簡単な例から始めましょう。まず、プロジェクトディレクトリに anonymous.go という名前のファイルを作成します。

cd ~/project
touch anonymous.go

anonymous.go を開いて、次のコードを追加します。

package main

import "fmt"

func main() {
    // 匿名関数を定義して変数 f に割り当てる
    f := func() {
        fmt.Println("hello world")
    }

    // 変数 f を通じて匿名関数を呼び出す
    f()
}

プログラムを実行します。

go run anonymous.go

期待される出力:

hello world

ここでは、func() {... } 構文を使って匿名関数を定義しました。この関数はパラメータを持たず、値も返しません。この匿名関数を変数 f に割り当てました。その後、f() を使って関数を呼び出します。これにより、匿名関数が実行され、"hello world" が出力されます。

匿名関数におけるパラメータの使用

匿名関数は通常の関数と同じようにパラメータを受け付けることができます。文字列型のパラメータを渡すようにコードを修正してみましょう。

anonymous.go の内容を以下のように置き換えます。

package main

import "fmt"

func main() {
    f := func(s string) {
        fmt.Println(s)
    }
    f("hello world")
}

プログラムを実行します。

go run anonymous.go

期待される出力:

hello world

今回は、匿名関数が文字列型のパラメータ s を受け取ります。func(s string) の部分は、匿名関数が文字列型の s という名前のパラメータを受け取ることを定義しています。f("hello world") を呼び出すとき、文字列 "hello world" が関数に渡され、その後コンソールに出力されます。これは、匿名関数に値を渡してそれらをより汎用的にする方法を示しています。

匿名関数からの値の返却

匿名関数も値を返すことができます。2つの整数をパラメータとして受け取り、それらの和を返す匿名関数を作成してみましょう。

anonymous.go の内容を以下のように置き換えます。

package main

import "fmt"

func main() {
    f := func(a, b int) int {
        return a + b
    }
    result := f(3, 5)
    fmt.Println(result)
}

プログラムを実行します。

go run anonymous.go

期待される出力:

8

今回の匿名関数のシグネチャは func(a, b int) int です。これは、2つの整数 (ab) を入力として受け取り、整数を出力として返すことを意味します。関数の本体である return a + b は、それらの和を計算して返します。f(3, 5) を呼び出すと、引数3と5で匿名関数が実行され、結果として8が返されます。その後、この結果を result 変数に格納してコンソールに出力します。

匿名関数を直ちに宣言して呼び出す

匿名関数を変数に割り当てることなく、一度に定義して呼び出すことができます。これは、迅速な一回限りの操作に便利です。

anonymous.go を更新します。

package main

import "fmt"

func main() {
    res := func(a, b int) int {
        return a + b
    }(3, 5) // ここで匿名関数を直接呼び出す
    fmt.Println(res)
}

プログラムを実行します。

go run anonymous.go

期待される出力:

8

ここでは、匿名関数 func(a, b int) int { return a + b } を定義し、関数宣言の直後に (3, 5) を追加することで直ちに呼び出しました。この構文 func(...) {...}(...) を使うと、1つの式で関数を定義して呼び出すことができます。丸括弧内の引数はすぐに関数に渡されます。この場合、3と5の和が返され、それが res 変数に割り当てられます。これは、単純な直ちに実行される関数に対する一般的な手法であり、初期化や短い計算に便利です。

コールバック関数としての匿名関数の使用

匿名関数はコールバック関数としても使用できます。これは、名前付き関数を作成することなく、関数の動作をカスタマイズしたい場合に便利です。

コールバック関数とは?

コールバック関数は、1つの関数に引数として渡され、最初の関数がそのタスクを完了した後に実行される関数です。これにより、呼び出し元は呼び出される関数の動作をカスタマイズでき、より柔軟性とモジューラリティが得られます。本質的には、コールバックを受け取る関数は、ある時点でコールバック関数を「戻して」呼び出します。

なぜ匿名関数をコールバック関数として使うのか?

匿名関数は、コールバック関数として非常にうまく機能します。なぜなら、匿名関数は通常、特定のコンテキスト内でのみ使用される短い特定の動作を表すからです。匿名関数をコールバック関数として使用することで、コードがより簡潔になり、別の名前付き関数を定義する必要がなくなります。

anonymous.go を以下のコードに置き換えます。

package main

import (
    "fmt"
    "math"
)

// 'visit'は、float64型のスライスと関数を受け取ります。それは、スライスの各要素に関数を適用します。
func visit(lst []float64, f func(float64)) {
    for _, value := range lst {
        f(value)
    }
}

func main() {
    arr := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}

    // 各要素をそれ自身と足し合わせる匿名関数を使用します
    visit(arr, func(v float64) {
        fmt.Printf("Sum:%.0f ", v+v)
    })
    fmt.Println()

    // 各要素をそれ自身で掛ける匿名関数を使用します
    visit(arr, func(v float64) {
        fmt.Printf("Product:%.0f ", v*v)
    })
    fmt.Println()

    // math.Powを使って各要素を二乗する匿名関数を使用します
    visit(arr, func(v float64) {
        v = math.Pow(v, 2)
        fmt.Printf("Square:%.0f ", v)
    })
    fmt.Println()
}

プログラムを実行します。

go run anonymous.go

期待される出力:

Sum:2 Sum:4 Sum:6 Sum:8 Sum:10 Sum:12 Sum:14 Sum:16 Sum:18
Product:1 Product:4 Product:9 Product:16 Product:25 Product:36 Product:49 Product:64 Product:81
Square:1 Square:4 Square:9 Square:16 Square:25 Square:36 Square:49 Square:64 Square:81

このプログラムでは、まず visit 関数を作成しています。この関数は、float64 型のスライス (lst) と func(float64) 型の関数 (f) を受け取ります。visit 関数は、スライスを反復処理し、各要素に対して提供された関数 f を呼び出します。このデザインパターンにより、visit 関数は提供されたコールバック関数 f に応じて異なるロジックを実行できます。

main 関数の中では、コールバックがどのように柔軟性を提供するかを示すために、異なる匿名関数を使って visit を3回呼び出しています。

  • 最初の匿名関数は、各要素をそれ自身と足し合わせます。
  • 2番目の匿名関数は、各要素をそれ自身で掛けます。
  • 3番目の匿名関数は、math.Pow を使って各要素を二乗します。

これは、匿名関数をコールバックとして渡す方法と、visit 関数がパラメータとして渡されたコールバック関数に基づいて異なるアクションを実行できる方法を示しています。これにより、コードがより再利用可能でモジューラーになります。

まとめ

この実験では、Go言語における匿名関数について学びました。匿名関数には名前がなく、しばしば短い、使い捨てのロジックに使用されます。それらは次のことができます。

  • 変数に割り当てて後で呼び出すことができます。
  • パラメータを受け取り、値を返すことができます。
  • 直ちに定義して呼び出すことができます。
  • 他の関数に引数として渡されるときにコールバックとして機能し、非常に柔軟でカスタマイズ可能な動作を可能にします。

匿名関数は、特にコードベースにたくさんの名前付き関数を散らばめることなく、即座にカスタムロジックが必要な場合に、柔軟性と便利さを提供します。それらを効果的に使用することで、より表現力があり、簡潔で、モジューラーなGoプログラムを作成することができます。クリーンで、読みやすく、より柔軟なコードを書くための強力なツールです。