##関数の基本構文
関数定義の基本形は下記のようになります。
func 関数名(引数) 戻り値型 {
処理の内容...
}
関数に対して引数を取り、関数の戻り値を指定して、処理内容を書いていく流れです。
##名前付き関数
では、簡単な関数を定義してみます。
func test(x, y int) int {
a := x * y
return a
}
func main() {
fmt.Println(test(10, 5)) //50
}
関数testをint型のx, yで定義し、戻り値型にint型を指定しています。変数aに処理を代入し、returnした値をmain関数で受け取り、値を指定して出力しています。
また、戻り値は複数返すことができます。
func test(x, y int) (int, int) {
a := x * y
b := x / y
return a, b
}
func main() {
a, b := test(10, 5)
fmt.Println(a, b) //50 2
}
下記のように、戻り値型を省略することで戻り値のない関数も定義できます。
func test() {
fmt.Println("こんにちは!")
return
}
func main(){
test() //こんにちは!
}
##エラー処理
エラー処理は下記のようなイディオムがあります。
import (
"os"
"log"
)
func main() {
_, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
}
このプログラムは、ファイルをオープンする際に、エラーがあればエラーメッセージとともに出力する処理です。そのため、osパッケージとlogパッケージをインポートしています。
##無名関数
無名関数は、明示的に定義する名前付き関数と違い、その名の通り「無名」で関数定義をします。
func main() {
a := func(x, y int) int { return x + y }
fmt.Println(a(2, 5)) //7
}
また、無名関数にそのまま引数を取ることで、さらに短く描くこともできます。
func main() {
fmt.Println(func(x, y int) int { return x + y }(2, 5)) //7
}
これは、関数リテラルを使用した無名関数定義でも、同じように出力します。
##関数で関数を返す
無名関数を使用することで、関数で関数を返すことができます。
func test() func() {
return func() {
fmt.Println("出力")
}
}
func main() {
a := test()
a() //出力
}
このプログラムは、引数も戻り値も持たない関数を戻り値として返した関数を、暗黙的に変数aに代入して出力しています。
##関数を引数に取った関数
同じように、関数を引数にとることも可能です。
func test(a func()) {
a()
}
func main() {
test(func() {
fmt.Println("出力") //出力
})
}
このプログラムでは、引数も戻り値も持たない関数を関数testの引数にとり、呼び出したものを、関数mainで無名関数として引数に渡すことで出力されています。
##クロージャーを応用したジェネレータ
Goでは、無名関数はクロージャーで、関数閉包のようなものです。通常、関数内で定義されている変数(ローカル変数)は関数の実行が完了したのちに破棄されるのですが、クロージャーと結びついていることで、クロージャーが参照されている限り破棄されることはないのです。このロジックを利用することで、Goにはないジェネレータのような機能を実現することが可能になります。
func test() func() int {
a := 0 //クロージャー内から参照されているローカル変数
return func() int {
a++
return a //ローカル変数(クロージャーに属する変数)を参照している
}
}
func main() {
b := test()
fmt.Println(b()) //1
fmt.Println(b()) //2
fmt.Println(b()) //3
fmt.Println(b()) //4
}
##最後に
今回は、関数についてまとめました!オブジェクト指向の機能のないGoでは関数の定義というのが中心的になるのではないでしょうか。
次からは、制御構文についてまとめようと思います!!
##参考文献
著者 松尾愛賀
翔泳社 スターティングGo言語