LoginSignup
24
17

More than 3 years have passed since last update.

golang 関数(func)の引数や戻り値で関数を指定したときの動作

Last updated at Posted at 2020-01-05

はじめに

golang を使い始めたのですが、関数の使い方で躓いたのでメモを残そうと思います!
関数の引数や戻り値に変数(int、string等)を指定した場合の挙動は想像できるのですが、 引数や戻り値に func(int) のような関数を指定をしたときの動きが直ぐにわからず、、動作を確認しました。

確認パターン

  • 引数に関数
  • 戻り値に関数

引数に関数(1)

簡単な足し算をする処理です。 関数 calc の第1引数を func(int, int) とし、関数を受け取れるようにしています。
どの順番で実行されたかを確認するために println を入れています。

package main

import (
    "fmt"
)

func calc(fn func(int, int) int, x int, y int) (res int) {
    fmt.Println("(A)")
    res = fn(x, y) // 関数:add を実行
    return
}

func add(x int, y int) int {
    fmt.Println("(B)")
    return x + y
}

func main() {
    fmt.Println(calc(add, 1, 2))
}

処理結果

(A)
(B)
3

次のことが分かりました。

  • main から calc(add, a, b) を呼んだときに実行されるのは関数 calc である(当たり前ですが、、)。
  • calc の中で res = fn(x, y)を実行したときに 関数 add が実行される。
  • 引数を fn func(int, int) とし、呼び出すときに calc(add, a, b) のように add を指定しているので、変数fn の中身は add になる。
  • 関数 add の戻り値で計算結果 3 を 関数 calc に返し、関数 calc はその値を、main に返している。

引数に関数(2)

次は足し算と引き算をする処理を見ていきます。

package main

import "fmt"

func calc(x int, y int, fn func(int, int) int) int {
    fmt.Println("(A)")
    return fn(x, y) // 関数:add or sub どちらかを実行
}

func add(x int, y int) int {
    fmt.Println("(B)")
    return x + y
}

func sub(x int, y int) int {
    fmt.Println("(C)")
    return x - y
}

func main() {
    resAdd := calc(1, 2, add)
    fmt.Println(resAdd)
    fmt.Println("-------------")

    resSub := calc(5, 4, sub)
    fmt.Println(resSub)
}

処理結果

(A)
(B)
3
-------------
(A)
(C)
1
  • 呼び出す時の引数により実行される関数が変わる。main から calc(1, 2, add) を指定した場合は、関数 add が実行され、calc(5, 4, sub) を指定した場合は、関数 sub が実行された。
  • 関数 calc の引数を fn func(int, int) int としている。この時点では何の関数が実行できるかは関数 calc 自身も分かっていない。同じ型の関数、且つ、呼び出し可能な関数であれば、何でも受け取ることを表す。つまり、呼び出し時のパラメータにより柔軟に呼び出す関数を変えることが可能となる。

戻り値が関数(1)

戻り値が関数になっている場合を確認します。
関数 func1 の戻り値を func() としました。


package main

import "fmt"

func func1(n string) func() {
    fmt.Println(n)
    return func() {
        func2("(B)")
    }
}

func func2(n string) {
    fmt.Println(n)
}

func main() {
    _ = func1("(A)")
}

処理結果

(A)
  • main にて func1("(A)") としている通り、関数 func1 が呼び出される。
  • 関数 func1 では受け取ったパラメータを元に (A) を出力。
  • 関数を返す必要があるため return func() としている。
  • この場合、関数 func2 は実行されない。

戻り値が関数(2)

関数 func2 が実行されるように変更します。

package main

import "fmt"

func func1(n string) func() {
    fmt.Println(n)
    return func() {
        func2("(B)")
    }
}

func func2(n string) {
    fmt.Println(n)
}

func main() {
    f := func1("(A)")
    fmt.Println("-----------")
    f()
}

処理結果

(A)
-----------
(B)
  • main を f := func1("(A)") にし、戻り値である func() を受け取る。関数 func1 が呼び出されただけでは 関数 func2 は実行されていない。
  • main にて f() を実行。これにより、関数 func1func2("(B)") がはじめて実行される。

戻り値が関数(3)

違うパターンを見てみます。

package main

import "fmt"

func func1(n string) func() {
    fmt.Println(n)
    return func2("(B)")
}

func func2(n string) func() {
    fmt.Println(n)
    return func() {
        fmt.Println(n)
    }
}

func main() {
    f := func1("(A)")
    fmt.Println("-----------")
    f()
}

処理結果

(A)
(B)
-----------
(B)
  • 関数 func1 の戻り値を return func2("(B)") としたことで、main から f := func1("(A)") が呼び出されたタイミングで関数 func2 も実行されるようになった。また、この時点では関数 func2 の return内の関数は実行されていないことが分かる。
  • main にて f() を実行することで、関数 func2 のreturn内の関数が実行される。また最初に func2 が呼び出された時の変数を保持しているため (B) が出力された。
24
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
17