📤

関数 ( Go入門連載 第6回 )

2021/03/06に公開

シリーズの目次

  1. Goの導入 for MacOS
  2. パッケージ
  3. 変数とデータ型
  4. 条件分岐
  5. ループ処理
  6. 配列
  7. 関数

関数

関数を利用するには関数を定義する必要があります。
Goの関数は func 関数名(引数 引数の型) 返り値の型 { 処理 }のように記載します。
他の言語と違い、変数名の後ろに型名を書くことに注意してください。

下の例では、add関数というものが定義され引数を2つ受け取り、その和を返しています。

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13)) // => 55
}

引数の型の省略

関数の2つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できます。

func add(x int, y int) int {

func add(x, y int) int {

とすることが可能です。

複数の返り値を返す

関数は複数の返り値を返すことができます。
複数の場合は型名は (...) で囲む必要があります。

次の例では 2つの string を返しています。

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b) // => world hello
}

名前付き返り値

Go言語では返り値となる変数に名前をつけることができます。返り値に名前をつけると、関数の最初で定義した変数名として扱われます。
名前をつけた返り値の変数を使うと、returnに何も書かずにその結果を返すことができます。

package main

import "fmt"

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(18)) // => 8 10
}

関数を変数に格納する

Go言語では関数を値として扱うことができて、変数へ格納することもできます。
まずその辺りの動作を以下確認しておきます。

例えば以下のような、引数で与えられた文字列を装飾して戻り値で返す関数があったとします

func decorate(src string) string {
    return "<<" + src + ">>"
}

func main() {
    f := decorate
    ret := f("golang")
    fmt.Println(ret) // => <<golang>>
}

無名関数

上の例では明確に関数名を持った関数を定義していますが、
関数名を与えずに無名関数として関数を定義して変数にセットすることができます。

func main() {
    f := func(src string) string { return "<<" + src + ">>" }
    ret := f("clang")
    fmt.Println(ret) // => <<clang>>
}

即時関数

無名関数を応用することで定義した瞬間に実行する関数を作ることができます。
この方法により一度実行するだけ終了する関数を作ることが可能です。

func main() {
    country := "日本"
    greeting := func(country string) string {
        switch country {
        case "日本":
            return "こんにちは"
        case "アメリカ":
            return "Hello"
        default:
            return "xxxxx"
        }
    }(country)

    fmt.Println(country)    // 日本
    fmt.Println(greeting)   // こんにちは
}

返り値として関数を返す関数

Go言語では 関数を返り値として返す関数 を定義することができます。
これにより一度変数に受けてその変数経由実行することも、変数を経由せずに実行することもできます。

func returnDecoreteFunc() func(string) string {
    return func(src string) string { return "||" + src + "||" }
}

func main() {
    // 一度変数に受ける場合
    f := returnDecoreteFunc()
    ret1 := f("Java")
    fmt.Println("decorete result ->", ret1) // => decorete result -> ||Java|| 

    // 変数を経由しない場合
    ret2 := returnDecoreteFunc()("C#")
    fmt.Println("decorete result ->", ret2) // => decorete result -> ||C#|| 
}

関数を引数にとる関数

Go言語では 関数を引数にとる関数 を定義することもできます。
以下の例では、第1引数に文字列、第2引数に装飾した文字列を返す関数 をとる関数(decorateAndPrint)で、
第1引数の文字列を第2引数の関数で装飾して、その結果を標準出力に書きだす処理をします。

func decorateAndPrint(str string, decoreteStrategy func(string) string) {
    decoratedStr := decoreteStrategy(str)
    fmt.Printf("original string: %s -> decorated string: %s\n", str, decoratedStr)
}

func main() {
    decorateAndPrint("Taro", func(src string) string {
        return "///" + src + "///"
    }) // => original string: Taro -> decorated string: ///Taro///

    decorateAndPrint("Taro", func(src string) string {
        return "<h2>" + src + "</h2>"
    }) // => original string: Taro -> decorated string: <h2>Taro</h2>
}

クロージャ

package main

import "fmt"

func main() {
    f := sumCalc()
    for i := 1; i < 5; i++ {
        sumVal, srcVal := f(i)
        fmt.Printf("add value: %d -> sum: %d\n", srcVal, sumVal)
    }
}

func sumCalc() func(int) (int, int) {
    sum := 0 // 足し算の合計値を格納する変数
    return func(x int) (int, int) {
        sum = sum + x
        return sum, x
    }
}

Discussion