Go 言語の関数
Go 言語の関数
Go では関数 (function) を次のように定義します。
func 関数名(引数) 戻り値の型 {
ステートメント
return 戻り値
}
引数を受け取らない場合は「引数」は省略できます。また、戻り値が無い場合は、「戻り値の型」や return 文は省略できます。
次の例では、 add という名前の関数を作成しています。
package main
import "fmt"
func main() {
z := add(1, 3)
fmt.Println(z) // 4
}
func add(x int, y int) int {
return x + y
}
add 関数は int 型の引数を二つ受け取り、 受け取った値を足し算してそれを、 int 型の戻り値として返しています。
これまで、おまじないのように書いてきた main 関数は、引数なし、戻り値なしの関数なので、 func main() { ... という型になっています。
Go の関数名
Go の関数名の命名には主に次のルールがあります。
- 名前の先頭はアルファベットで始める
- Go のキーワードと同じ名前は付けられない
- 関数名に記号は使用できない
関数名の先頭は大文字・小文字どちらでも構いませんが、関数を定義したパッケージの外部から呼び出す関数名は大文字で始める必要があります。
Go では名前をつけない関数も作成できます。詳しくは「Go 言語の無名関数とクロージャ」をご覧ください。
Go の関数で複数の戻り値を返す
Go の関数では、複数の戻り値を呼び出し元に返すことができます。
複数の戻り値を返す時には、まず関数の「戻り値の型」を (型1, 型2) のように、 () の中に戻り値の型をカンマで区切って並べます。
戻り値を複数返す箇所では、 return に続き複数の値をカンマで区切って返します。
呼び出し元で戻り値を複数受け取る場合は、戻り値を受け取る変数をカンマで区切って並べます。
次の例の add 関数では、 int の値を三つ返しています。
package main
import "fmt"
func main() {
m, n, z := add(1, 3) // 戻り値を三つ受け取る
fmt.Println(m, n, z) // 1 3 4
}
func add(x int, y int) (int, int, int) {
return x, y, x + y // 値を三つ返す
}
Go の関数の戻り値を無視する
戻り値が複数の関数を呼ぶ場合は、基本的に全ての戻り値を受け取る必要があります。
戻り値がひとつだけの関数を呼ぶ場合」、戻り値は必ずしも受け取る必要がありません。
ところが、必ずしも戻り値の全てが役に立つとか、興味があるとも限らないですよね。
Go では複数の戻り値の中で不要な戻り値はアンダースコア _ で受け取ることで 無駄な変数を宣言せずに済ませることができます。
例えば、上の例で add 関数が返す戻り値の中で、 足し算の結果 (三つ目の戻り値) だけを受け取りたい場合は、次のようにします。
_, _, z := add(1, 3)
Go で可変長引数の関数を作る方法
Go では可変長の引数を受け取る関数を作るのは簡単です。
Go で可変長の引数を受け取る関数を定義するには、引数を ...型 という形で宣言します。
こうすることで、呼び出した時にその引数には、その型のスライスとして値が渡されます。
Go の「スライス」というのは、動的な配列のようなものです。詳しくは「Go のスライス」を参照してください。
次の例では、 add関数は可変長引数を、int のスライスとして受け取っています。
package main
import "fmt"
func main() {
z := add(1, 3, 5, 7)
fmt.Println(z)
}
func add(x int, params ...int) int {
fmt.Printf("* %v : %T\n", params, params)
for i, j := range params {
fmt.Printf("* [i,j]=[%d,%d]\n", i, j)
x += j
}
return x
}
実行結果は次のようになります。
go run main.go
* [3 5 7] : []int
* [i,j]=[0,3]
* [i,j]=[1,5]
* [i,j]=[2,7]
16
ここでは for-range ループでスライスの中身を取り出しています。 for-range ループについては Goの for range ループを参照してください。
Go 言語の関数は値渡し
このセクションを理解するには、ポインターについて理解している必要があります。 ポインターについては「Go 言語のポインター」を参照してください。
Go では 数値型 (int、float64 等)、文字列型、構造体 (struct) などの型では、基本的に値渡し (call by value) になります。
つまり、関数を呼び出す時の引数には値がコピーされ、呼び出された関数ではコピーされた値を使います。このため、 関数側で値を書き換えても、元の変数には影響しません。
package main
import "fmt"
func main() {
x := 0
fmt.Println("x = ", x) // x = 0
foo(x)
fmt.Println("x = ", x) // x = 0 (変わらない)
}
func foo(x int) {
x++ // 1 を足しているが...
}
もし、関数内で呼び出し元の変数を書き換えたいなら、引数を参照で渡す必要があります。
package main
import "fmt"
func main() {
x := 0
fmt.Println("x = ", x) // x = 0
foo(&x) // x の参照を渡している
fmt.Println("x = ", x) // x = 1
}
func foo(x *int) {
*x++
}
一方、マップ (map) や スライス (slice) の場合は、参照渡し (call by reference) になります。
package main
import "fmt"
func main() {
a := []int{1, 2, 3}
fmt.Println("a = ", a) // a = [1 2 3]
foo(a)
fmt.Println("a = ", a) // a = [99 2 3]
}
func foo(a []int) {
a[0] = 99
}
以上、ここでは Go の関数の基礎について説明しました。 無名関数やクロージャなどの少し進んだトピックについては「Go 言語の無名関数とクロージャ」をご覧ください。