関数

関数 の Go

1個の演習

関数について

関数を使うと、コードを再利用できる単位にまとめることができます。 関数は、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文を書くと、それらの値が返されます。 これは「裸の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では、関数は第一級オブジェクトとみなされるため、とても強力です。 関数に関しては、メソッドや無名関数など、ほかにもさまざまなコンセプトがあり、この後の学習で出てきます。

GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

関数を学習する