はじめに
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()
を実行。これにより、関数func1
のfunc2("(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)
が出力された。