Golangの変項関数とは何ですか?
Golangにおける可変長引数を持つ関数は、基本的には他の関数と同じです。
唯一の違いは、関数を呼び出すときに未知の数の引数を渡すことができるようにパラメータを定義していることです。
可変長引数の関数を宣言する方法
可変長引数を持つ関数の宣言はシンプルで簡単です。
関数に任意の数の引数を渡すためにpack演算子を使います。そうすることで、その関数の中でスライスが受け渡されます。
以下は、そのように関数を宣言する基本的な書き方です。
func 関数名(param ...paramType) 戻り値の型 {}
元々付属している可変長引数の関数
私たちが普段使っている可変長引数の関数はいくつかあります。
それらはすべて組み込みであり、例としてfmtパッケージのPrintf関数を実際に使ってみましょう。
package main
import (
"fmt"
)
func main() {
fmt.Printf("Hello world, the number is: %d, the float is: %f", 42, 161.9999) // Hello world, the number is: 42, the float is: 161.999900
}
上記の関数は、第1引数が文字列、第2引数以降が任意の個数の引数を取るという引数が可変長となる関数です。
可変長関数はどの場面で使うべき?
ある関数が特定の数の引数に縛られない場合はいつでも、その関数は可変長引数関数となることができます。
そのため可変長引数の関数は、引数がいくつ渡されるかわからない時に使うとよいでしょう。
可変長関数に引数を渡す方法
可変長関数は任意の数の引数を取ることができ、任意の型の引数を取ることができます。
また、引数に様々な種類の型を与える場合は、インタフェースが役立ちます。
以下がそのコード例です。
package main
import (
"fmt"
)
func variadicFunc(arg ...int) {
for i := range arg {
fmt.Println(arg[i])
}
}
// インターフェースのスライスを返す
func printAll(arg ...interface{}) {
fmt.Println(arg...)
}
func main() {
variadicFunc(1, 2) // 2つの引数
// 出力結果:
// 1
// 2
variadicFunc(3, 4, 5, 6, 7) // 5つの引数
// 出力結果:
// 3
// 4
// 5
// 6
// 7
// インターフェースを使うことで引数の型がなんでもOKになる
printAll(1, "Tanaka", 3.5, 4.1820, 'a') // 1 Tanaka 3.5 4.182 97
}
pack演算子・unpack演算子
可変長引数の使用中に少し戸惑うと思われる演算子が2つあります。pack演算子とunpack演算子です。
pack演算子は可変長引数関数を定義するときに使用され、引数はその関数に渡される時にスライスにまとめられます。
unpack演算子は引数に渡されたスライスの中身を解凍します。
以下は、両方の演算子がどのように機能するかを示すコード例です。
package main
import (
"fmt"
)
func v1(names ...string) { // pack演算子
fmt.Println(names)
}
func main() {
v1("Tanaka", "Suzuki", "Sato", "Kato") // [Tanaka Suzuki Sato Kato]
var names []string = []string{"Sakura", "Tajima"}
// unpack演算子
v1(names...) // [Sakura Tajima]
}
可変長関数で渡された引数にアクセス(取得)する方法
可変個体関数の引数にアクセスするのはとても簡単です。
以下のように、インデックスを付けてアクセスすることができます。
package main
import (
"fmt"
)
func test(arg ...int) {
// 1つ目の引数
fmt.Println(arg[0])
// 2つ目の引数
fmt.Println(arg[1])
}
func main() {
test(1, 2, 3) // 1 2
}
渡された引数の値を書き換える
可変長関数が引数を取る際、それらはスライスに追加されます。
そして、スライスは本質的にGoの配列への参照なので、渡されたパラメータが何らかの形で変更されると、元のデータも変更されます。
package main
import (
"fmt"
)
func test(arg ...int) {
// sliceの中身を出力する
fmt.Println(arg)
// sliceのデータを書き換える
arg[0] = 0
}
func main() {
var numbers []int = []int{1, 2, 3}
fmt.Println(numbers)
// 出力結果:
// [1 2 3]
test(numbers...)
// 出力結果:
// [1 2 3]
fmt.Println(numbers)
// 出力結果:
// [0 2 3]
}
可変長非異数と非可変長の引数を渡す方法
非可変長の引数は可変長の引数と一緒に渡すことができます。
書き方としては以下のように、すべての非可変長の引数を渡し、次に可変長の引数を渡します。
package main
import (
"fmt"
)
// 先に非可変長の引数、最後に可変長の引数を渡す
func test(i int, s string, num ...int) {
fmt.Println(num)
}
func main() {
test(1, "Asahi", 3, 4) // [3 4]
}