関数を使うと、コードを再利用できる単位にまとめることができます。
関数は、funcキーワード、関数名、そして丸括弧(())の中にカンマで区切って並べた0個以上の仮引数とその型からなります。
仮引数には、必ず型を明示する必要があります。仮引数の型推論は行われません。 仮引数にデフォルト値はないので、関数の仮引数はすべて必須です。
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// One parameter
func PrintHelloName(name string) {
fmt.Println("Hello " + name)
}
同じ型の仮引数は、まとめて宣言し、そのあとに型を1つだけ書くことができます。
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
よく混同されがちな2つの用語、parametersとargumentsについて、簡単に確認しておきましょう。
仮引数とは、関数のシグネチャで定義される名前のことです。たとえば、上の関数PrintGreetingNameではgreetingとnameが仮引数です。
入力とは、関数を呼び出すときに仮引数へ渡す具体的な値のことです。
たとえば、次の例では"Hello"と"Katrina"が、仮引数greetingとnameに渡される入力です。
PrintGreetingName("Hello", "Katrina")
仮引数のあとには、0個以上の戻り値を書きます。戻り値にも型を明示する必要があります。
戻り値が1つのときはそのまま書き、複数あるときは括弧で囲みます。
値は、returnキーワードを使って、関数から呼び出し元のコードに返されます。
1つの関数の中にreturn文を複数書くこともできます。
関数の実行は、return文のいずれかに到達した時点で終了します。
複数の値を返す場合は、カンマで区切って並べます。
複数の戻り値の慣用的な使い方について詳しくは、リンク先のコンセプトを参照してください。
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
関数を呼び出すには、関数名を書き、括弧の中に各仮引数に対応する入力を渡します。
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
仮引数と同様に、戻り値にも名前を付けることができます。
名前付き戻り値を使う場合、引数なしのreturn文を書くと、それらの値が返されます。
これは「裸のreturn」と呼ばれます。
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
値渡しと参照渡しの違いについても、はっきりさせておきましょう。
まずは値渡しから見てみましょう。
次の例では、変数valを関数MultiplyByTwoに渡すとき、valのコピーが渡されます。
そのため、newValは更新された値4を持ちますが、元の変数valは2のままです。
内部的には、Goは元の値のコピーを作るので、関数によって変更されるのはこのコピー、つまりvだけです。
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
厳密に言えば、Goではすべての入力が値渡しで渡されます。つまり、関数に渡された値やデータのコピーが作られます。
しかし、関数に渡すデータのコピーを作らず、関数の中でそのデータを変更したい場合は、入力としてpointersを使います。これが参照渡しです。
参照渡しを実現するには、pointerを使います。
pointerを入力として関数に渡すと、データのコピーを操作するのではなく、関数に渡された元のデータを変更できます。
今のところは、ポインター型は関数のシグネチャで型の前に付く*で見分けられる、とだけ知っておけば十分です。
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
pointerの考え方が難しく感じても、心配いりません。
シラバスの後半に、ポインターをマスターするための専用のセクションがあります。
slicesとmapsは、上で述べたルールの例外であることに注意してください。
sliceやmapを入力として関数に渡すと、型に*が明示されていなくても、ポインター型として扱われます。
つまり、スライスやマップを関数に渡してその元のデータを変更すると、その変更は元のスライスやマップにも反映されます。
Goでは、関数は第一級オブジェクトとみなされるため、とても強力です。 関数に関しては、メソッドや無名関数など、ほかにもさまざまなコンセプトがあり、この後の学習で出てきます。