このセクションでは、変数パラメーターの使用法を紹介します。変数パラメーターを適切に使用すると、特にログ関数などの入出力関数のコードがシンプルで使いやすくなります。
可変個引数型
可変パラメーターとは、関数によって渡されるパラメーターの数が可変であることを意味します。これを行うには、まず関数を可変パラメーターを受け入れることができる型として定義する必要があります。
func myfunc(args ...int){
for _, arg := range args {
fmt.Println(arg)
}
}
上記のコードは、関数 myfunc() が可変数のパラメーター (すべて int 型) を受け入れることを意味するため、次のように呼び出すことができます。
myfunc(2, 3, 4)
myfunc(1, 3, 7, 13)
...type
の形式の型は、関数のパラメータ型としてのみ存在でき、最後のパラメータである必要があります。これは構文糖衣 (構文糖衣) です。つまり、この構文は関数の関数には影響しません。一般に、プログラマは、プログラムの可読性を高めるために糖衣構文を使用し、それによってプログラム エラーの可能性を減らします。
内部実装メカニズムに関して言えば、 type ...type
本質的に配列スライス、つまり[]type
です。そのため、上記のパラメーター args は for ループを使用して各受信パラメーターを取得できます。
...type
の糖衣構文がなければ、開発者は次のように記述する必要があります。
func myfunc2(args []int) {
for _, arg := range args {
fmt.Println(arg)
}
}
関数の実装の観点からは、これは何の効果もありません。好きなように記述できますが、呼び出し元の観点からは、状況はまったく異なります。
myfunc2([]int{1, 3, 7, 13})
配列スライス インスタンスを構築するには[]int{}
を追加する必要があることがわかりますが、 ...type
の糖衣構文を使用すると、自分で処理する必要がありません。
任意の型の可変引数
前の例では、変数パラメータの型は int に制限されています。任意の型を渡したい場合は、interface{} として型を指定できます。以下は、Go 言語の標準ライブラリの fmt.Printf() の関数プロトタイプです。 :
func Printf(format string, args …interface{}) {
// …
}
Interface{} を使用して任意のタイプのデータを渡すことは、Go 言語の一般的な使用法です。interface{} の使用は依然としてタイプセーフですが、これは C/ C++とは異なります。例を使用して、タイプのデータを割り当てる方法を理解しましょうインターフェース{} 。
package main
import "fmt"
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 234
var v3 string = "hello"
var v4 float32 = 1.234
MyPrintf(v1, v2, v3, v4)
}
このプログラムの出力は次のとおりです。
1 is an int value.
234 is an int64 value.
hello is a string value.
1.234 is an unknown type.
変数パラメータのリストを走査 – 各パラメータの値を取得します
変数パラメーター リストの数は固定されておらず、受信パラメーターはスライスです。各パラメーターの特定の値を取得する必要がある場合は、変数パラメーター変数を走査できます。次のコードを参照してください。
package main
import (
"bytes"
"fmt"
)
// 引数の数は0〜nであり、型制約は文字列である関数を定義する
func joinStrings(slist ...string)string {
// バイトバッファを定義して、文字列を高速に接続する
var b bytes.Buffer
// 可変引数リスト([]string型)を繰り返します。
for _, s:= range slist {
// 反復処理された文字列をバイト配列に連続して書き込みます
b.WriteString(s)
}
// 接続されたバイト配列を文字列に変換し、出力します。
return b.String()
}
func main(){
// 3つの文字列を入力し、1つの文字列に連結します
fmt.Println(joinStrings("pig "、"and"、"rat"))
fmt.Println(joinStrings("hammer"、"mom"、"and"、"hawk"))
}
コード出力は次のとおりです。
pig and rat
hammer mom and hawk
コードの説明は次のとおりです。
- 行 8 は、可変パラメーターを持つ関数を定義しています。slist の型は []string で、各パラメーターの型は string です。つまり、関数はパラメーターとして文字列型のみを受け入れます。
- 11 行目の bytes.Buffer は、この例では StringBuilder のように動作し、文字列連結操作を効率的に実行できます。
- 行 13 は slist の変数パラメーターを調べます。s は各パラメーターの値で、その型は文字列です。
- 15 行目で、各受信パラメータを bytes.Buffer に入れます。
- 19行目ではbytes.Buffer内のデータを文字列に変換し、関数の戻り値として返しています。
- 24 行目で、3 つの文字列を入力し、joinStrings() 関数を使用してパラメータを文字列出力に連結します。
- 25行目に4文字の文字列を入力し、接続後に出力します。
可変パラメータの数を取得したい場合は、len() 関数を使用して、可変パラメータの変数に対応するスライスの長さを計算し、可変パラメータの数を取得できます。
get variadic type – 各パラメータの型を取得します
変数パラメータがインターフェース{}型の場合、任意の型の値を渡すことができます。このとき、変数の型を取得する必要がある場合は、スイッチを介して変数の型を取得できます。次のコードは、異なる型の一連の値を渡す printTypeValue() 関数。異なるパラメーターの値と型の詳細な説明をそれぞれ出力します。
印刷タイプと値:
package main
import(
"bytes"
"fmt"
)
func printTypeValue(slist ...interface{}) string {
//バイトバッファを高速文字列接続として使用します
var b bytes.Buffer
//パラメータを繰り返します
for _, s := range slist {
//interface {}タイプを文字列にフォーマットします
str := fmt.Sprintf("%v"、s)
//タイプの文字列説明
var typeString string
//sの型をアサーションします
switch s.(type) {
case bool:// sがブール型の場合
typeString = "bool"
case string:// sが文字列型の場合
typeString = "string"
case int:// sが整数型の場合
typeString = "int"
}
//文字列接頭辞を書く
b.WriteString("value: ")
//値を書き込む
b.WriteString(str)
//タイプ接頭辞を書く
b.WriteString(" type: ")
//タイプ文字列を書き込む
b.WriteString(typeString)
//改行を書き込む
b.WriteString("\ n")
}
return b.String()
}
func main() {
//printTypeValue()を介して異なるタイプの変数を印刷します
fmt.Println(printTypeValue(100、 "str"、 true))
}
コード出力は次のとおりです。
value: 100 type: int
value: str type: string
value: true type: bool
コードの説明は次のとおりです。
- 8行目のprintTypeValue()は、さまざまなタイプの値を受け取り、タイプと値の説明を出力します。
- 行 11、bytes.Buffer バイトは高速文字列連結としてバッファーされます。
- 14 行目、slist の各要素を走査します。タイプはinterface{}です。
- 17 行目で、fmt.Sprintf を
%v
動詞とともに使用して、interface{} 形式の値を文字列に変換します。 - 20行目で変数の型名として文字列を宣言しています。
- 23 行目の switch s.(type) は、interface{} 型に対して型アサーションを実行できます。つまり、変数の実際の型を決定します。{}
- 24行目から29行目はs変数の取り得る型で、それぞれの型に対応する型文字列をtypeStringに代入しています。
- 33行目から42行目は出力フォーマットを記述する処理です。
可変パラメータ変数は、すべてのパラメータを含むスライスです。この可変パラメータ変数を次の可変パラメータ関数に渡したい場合は、渡すときに可変パラメータ変数の後に...
を追加すると、要素を渡すことができます。可変個引数変数自体の代わりにスライスが渡されます。
次の例では、print() 関数と実際に呼び出される rawPrint() 関数をシミュレートします。どちらの関数にも可変パラメータがあり、print から rawPrint に渡す必要があります。
変数パラメータの受け渡し:
package main
import "fmt"
//実際に印刷される関数
func rawPrint(rawList ...interface{}) {
//可変パラメーターセットを反復処理
for _, a := range rawList {
//パラメータを印刷する
fmt.Println(a)
}
}
//印刷関数の包装
func print(slist ...interface{}) {
// slistを次の関数に完全に渡すために可変引数セットをrawPrintに渡します
rawPrint(slist...)
}
func main() {
print(1, 2, 3)
}
コード出力は次のとおりです。
1
2
3
コードの説明:
- 9行目から13行目は、rawPrint()のパラメータリストrawListを走査して出力します。
- 20 行目では、print の変数パラメータ リストに
...
を追加した後、変数が rawPrint() に渡されます。 - 25行目で、1、2、3の3つの整数値を渡して出力します。
20 行目を次のように変更しようとすると、
rawPrint("fmt", slist)
コードを再度実行すると、次のように出力されます。
[1 2 3]
この時点で、slist (type []interface{}) は全体として rawPrint() に渡され、rawPrint() 関数内で渡される変数も slist のスライス値になります。
...
を使用して渡される変数パラメータは、スライス間の追加接続を使用する場合と同じ機能です。