関数は、どのプログラミング言語においても必要不可欠なものです。関数は、コードを構造化し、日常的な作業を容易にします。
Goは、「ファーストクラス関数」をサポートしています。つまり、Goの関数は、変数に代入したり、引数として渡したり、他の関数から返したりすることができます。
関数の宣言と呼び出し
関数を宣言するには、funcキーワードを使います。関数宣言の一般的な構造を以下に示します。
func nameOfFunction(arguments) (returnTypes) {
// body
}
コンソールに “Hello World!”と表示する関数を書いてみましょう。
package main
import (
"fmt"
)
func main() {
helloWorld() // call function
}
func helloWorld() { // declare the function
fmt.Println("Hello World!") // prints "Hello World!"
}
ご覧のように、関数を呼び出すのはとても簡単です。
returnの型と引数
Goの関数は、任意の戻り値の型と任意の引数の型を持つことができます。これらを宣言する方法を見てみましょう。
package main
import (
"fmt"
)
func main() {
greetUser("John") // prints "Hello, John"
fmt.Println(add10(12)) // prints 22
}
func greetUser(user string) {
fmt.Printf("Hello, %s\n", user)
}
func add10(value int) int {
return value + 10
}
上のコードでは、greetUser()関数は戻り値の型が宣言されていません。しかし、string型の引数が与えられています。関数add10()では、パラメータはint型であり、戻り値もint型です。
関数内の複数の引数
関数の内部では、複数の引数を渡すことができます。その方法の例を見てみましょう。
package main
import (
"fmt"
)
func main() {
showAge("Paul", 22) // prints "Paul is 22 years old."
fmt.Println(multiplyTwoNumbers(12, 23)) // prints 276
}
func showAge(name string, age int) {
fmt.Printf("%s is %d years old.\n", name, age)
}
func multiplyTwoNumbers(x, y int) int { // same type of arguments
return x*y
}
複数の戻り値を持つGoの関数
関数は、必要な数の値を返すことができます。ここでは、その方法を示す例を紹介します。
package main
import (
"fmt"
)
func main() {
vAdd, vSub := addSub(35, 25)
fmt.Printf("35 + 25 = %d\n", vAdd) // prints "35 + 25 = 60"
fmt.Printf("35 - 25 = %d\n", vSub) // prints "35 - 25 = 10"
}
func addSub(x, y int) (int, int) { // multiple return values (int, int)
return x+y, x-y
}
Goでの名前付き戻り値
GoLangでは、関数の戻り値に名前を付けることができます。これは、変数の名前を言及することができますので ここでは、それを行う例を紹介します。
package main
import (
"fmt"
)
func main() {
fmt.Println(divby10(100))
}
func divby10(num int) (res int) {
res = num/10
return res
}
「値」と「参照」による関数
Goでは、関数はパラメータを値または参照で受け取ることができます。値を取る場合はどうなるか見てみましょう。
package main
import (
"fmt"
)
func main() {
val := 12
fmt.Printf("The value before function call is %d\n", val)
changeValue(val) // does not change value
changeValueByRef(&val) //changes value
fmt.Printf("The value after function call is %d\n", val)
}
func changeValue(num int) {
num = 42
}
func changeValueByRef(num *int) {
*num = 42
}
上のコードでは、関数が値を受け取るときには、値をまったく変更しません。しかし、参照を取ると値が変わります。
関数からエラーを返す
関数はエラーを返すことができます。エラーは、予期せぬことが起こったことを示す手段です。だから、それを持つことが必要なのです。
空白識別子の使用
空白の識別子が使われているのは、変数が宣言されているのに使われていないと、Goが警告を出すからです。構文としては、「_」を使って値を代入します。
package main
import (
"fmt"
)
func main() {
j, _ := returnTwoNumbers() // ignores the second return value
fmt.Println(j)
}
func returnTwoNumbers() (int, int) {
return 12, 23
}
Golangの無名関数
Goはファーストクラス関数をサポートしているので、関数を変数に代入したり、すぐに呼び出したりすることができます。以下にコード例を示します。
package main
import (
"fmt"
)
func main() {
getMod := func(a, b int) (int) { // declare it
return a%b
}
fmt.Println(getMod(12, 5)) // prints 2
// call by its name
}
関数の即時呼び出し
以下のコードでは、無名関数が直ちに呼び出されています。
package main
import (
"fmt"
)
func main() {
func(name string) {
fmt.Printf("Hello, %s", name)
}("John") // prints "Hello, John"
}
「defer」キーワードについて
“Defer “は、関数の呼び出しを周囲の関数が戻るまで遅らせるキーワードです。
package main
import "fmt"
func main() {
defer fmt.Println("before this.") // runs at the end
fmt.Println("This is printed ") // runs first
}
ユーザー定義のファンクションタイプ
ユーザー定義関数型は、関数として識別できる型です。以下はその例です。
package main
import (
"fmt"
)
type First func(int) int // declare type
func getFunction() First { // use it
return func(val int) int {
return val * 5
}
}
func main() {
f := getFunction() // returns a function of type First
fmt.Println(f(12))
}
引数としての機能
Goの関数は、引数として渡すことができます。これにより、高階の関数を作ることができます。
関数から関数を返す
関数は、別の関数を返すこともできます。その方法をご紹介します。
package main
import (
"fmt"
)
func main() {
f := getFunction("John") // returns a function
f() // prints "Hello, John"
}
func getFunction(name string) func() {
return func() {
fmt.Printf("Hello, %s", name)
}
}
関数クロージャーについて
関数クロージャは、理解すべき重要な概念です。よりよいコードを作成したり、言語に対する理解を深めたりするのに役立ちます。
では、クロージャとは何でしょうか?
クロージャとは、そのスコープ内の変数に束縛される関数のことです。つまり、それぞれのクロージャが独自のスコープを持っているということです。