Go言語の関数の定義を扱いましたが、予め定義した数だけの引数を扱っていました。
では、その引数に任意の数だけ渡すとなるとどうなるでしょうか。
関数でこの任意の数の引数を扱って定義するものを、可変長引数(Variadic parameter)といいます。
ここでは、この可変長引数についてみていきましょう。
可変長引数(Variadic parameter)
Go言語では、無制限の数の引数をとる関数を定義することができます。これを可変長引数(Variadic parameter)といいます。
これまでは、次のコードのように、関数に渡す引数の数は決まっていました。
func calc(x int, y int) int {
return x + y
}
ここでは、int型のxとyという2つの引数を渡しているわけです。
この引数の数に制限を設けないのが可変長引数です。
可変長引数を使った関数は、次のような形で定義します。
func 関数名(引数名 ...型名) 型名 {
処理コード
}
引数に型名を指定する時に、「…」を前につけることで関数を宣言します。
具体的なコードでやってみましょう。引数の値の総和を求める関数を定義してみます。
package main
import "fmt"
func sum(n ...int) int {
fmt.Println(n)
fmt.Printf("%T\n", n)
sum := 0
for i, v := range n {
sum += v
fmt.Println("インデックス:", i, "値:", v, "・・・現在の合計:", sum)
}
return sum
}
func main() {
x := sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
fmt.Println("引数で渡した数の総和:", x)
}
関数名をsum()にしました。引数nをint型の可変長引数で定義しているのがわかります。
nの値と型名を確認のためにPrintln()とPrintf()で出力しています。
合計を求める関数なので、sumという変数を0で初期化して、for文を使って値を一つずつ取り出して順に加えていく処理を、sum += v で行っています。
ループ処理で、インデックス、値、その時の合計を出力して確認しています。
最後にreturnで合計の値を返しています。
これをmain関数で呼び出して、可変長引数として1から10までの数字を渡して実行しています。
実行すると次のように出力されます。
[1 2 3 4 5 6 7 8 9 10]
[]int
インデックス: 0 値: 1 ・・・現在の合計: 1
インデックス: 1 値: 2 ・・・現在の合計: 3
インデックス: 2 値: 3 ・・・現在の合計: 6
インデックス: 3 値: 4 ・・・現在の合計: 10
インデックス: 4 値: 5 ・・・現在の合計: 15
インデックス: 5 値: 6 ・・・現在の合計: 21
インデックス: 6 値: 7 ・・・現在の合計: 28
インデックス: 7 値: 8 ・・・現在の合計: 36
インデックス: 8 値: 9 ・・・現在の合計: 45
インデックス: 9 値: 10 ・・・現在の合計: 55
引数で渡した数の総和: 55
可変長引数で渡したものは、スライスになっているのがわかります。ここでは[]intのスライスです。合計値が計算されて出力されています。
スライスの展開
今度は、可変長引数のスライスそのものを渡す場合についてみて行きます。
上のコードを次のように少し変更してみます。
package main
import "fmt"
func sum(n ...int) int {
fmt.Println(n)
fmt.Printf("%T\n", n)
sum := 0
for _, v := range n {
sum += v
}
return sum
}
func main() {
s := []int{10, 20, 30, 40, 50}
result := sum(s...)
fmt.Println("結果:", result)
}
sum()関数の基本的なところは先ほどと変わっていません。可変長引数での関数で、for文でのインデックスをアンダースコアに変えて利用しないことにし、ループ毎の出力を削除しただけで、計算そのものは同じです。
これをmain()関数で呼び出すのですが、変数sでスライスを定義しています。この変数をsum()関数に渡して実行するのですが、sum(s)ではなく、sum(s…)となっていることに注意です。
実行するとこうなります。
[10 20 30 40 50]
[]int
結果: 150
思った結果が出力されていると思います。このようにして、スライスは展開します。
スライスを可変長引数として渡す時は、「…」とスライスに続けて渡すということに注意です。
もし、次のように、sum(s…)ではなく、sum(s)として呼び出した場合はどうなるでしょうか?
func main() {
s := []int{10, 20, 30, 40, 50}
result := sum(s)
fmt.Println("結果:", result)
}
この場合は、実行すると次のようにエラーになります。
cannot use s (type []int) as type int in argument to sum
このように、可変長引数の関数でスライスを引数として展開するには、「…」を付けて渡すことに注意しましょう。
Pythonコードで書くと…
Pythonでの可変長引数についてやってみましょう。
Pythonでの可変長引数は、関数定義時の引数名に *や** をつけると可変長引数として扱われます。
*args、*kwargsという使われ方をすることが多いです。タプル型として渡されることになります。
Goでのコードを次のように書き換えてみました。
def sum(*args):
print(args)
print(type(args))
sum = 0
for i in args:
sum += i
print("現在の合計:", sum)
return sum
def main():
x = sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print("引数で渡した数の総和:", x)
if __name__ == "__main__":
main()
実行すると以下のように出力されます。
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
<class 'tuple'>
現在の合計: 1
現在の合計: 3
現在の合計: 6
現在の合計: 10
現在の合計: 15
現在の合計: 21
現在の合計: 28
現在の合計: 36
現在の合計: 45
現在の合計: 55
引数で渡した数の総和: 55
最後に
Go言語の関数において、可変長引数(Variadic parameter)についてみてきました。
無制限の数の引数をとる関数を定義することができるのが、可変長引数です。
func 関数名(引数名 …型名) 型名 {処理コード} のように、引数の型名を指定する時に、「…」を直前に付けて定義します。
スライスを可変長引数の関数に渡す時は、スライスの変数に続けて「…」を記述します。