前書き

_variadic function_は、1つ以上の値を1つの引数として受け入れる関数です。 可変引数関数は一般的なケースではありませんが、コードをよりクリーンで読みやすくするために使用できます。

可変長関数は、見かけよりも一般的です。 最も一般的なものは、https://golang.org/pkg/fmt [+ fmt +]パッケージの `+ Println +`関数です。

func Println(a ...interface{}) (n int, err error)

省略記号のセット( `+ …​ + `)は可変機能と見なされます。 省略記号は、指定されたパラメーターが0、1、またはそれ以上の値であることを意味します。 `+ fmt.Println `パッケージの場合、パラメーター ` a +`は可変引数であると述べています。

`+ fmt.Println +`関数を使用して、0、1、または複数の値を渡すプログラムを作成しましょう。

print.go

package main

import "fmt"

func main() {
   fmt.Println()
   fmt.Println("one")
   fmt.Println("one", "two")
   fmt.Println("one", "two", "three")
}

最初に `+ fmt.Println `を呼び出すとき、引数を渡しません。 2回目に ` fmt.Println `を呼び出すときは、値が ` one `の単一の引数のみを渡します。 次に、「 one 」と「 two 」を渡し、最後に「 one 」、「 two 」、「 three +」を渡します。

次のコマンドでプログラムを実行しましょう:

go run print.go

次の出力が表示されます。

Output
one
one two
one two three

出力の最初の行は空白です。 これは、 `+ fmt.Println `が最初に呼び出されたときに引数を渡さなかったためです。 2回目に「 one 」の値が出力されました。 次に、「 one 」と「 two 」、最後に「 one 」、「 two 」、および「 three +」。

可変個の関数を呼び出す方法がわかったので、独自の可変個の関数を定義する方法を見てみましょう。

可変長関数の定義

引数の前に省略記号( + …​ +)を使用することにより、可変引数関数を定義できます。 関数に名前が送信されたときに挨拶するプログラムを作成してみましょう。

hello.go

package main

import "fmt"

func main() {
   sayHello()
   sayHello("Sammy")
   sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
   for _, n := range names {
       fmt.Printf("Hello %s\n", n)
   }
}

+ names`と呼ばれる単一のパラメーターのみを取る + sayHello`関数を作成しました。 データ型の前に省略記号( + …​ +)を置くため、パラメーターは可変引数です: + …​ string +。 これは、関数が0、1、または多くの引数を受け入れることができることをGoに伝えます。

+ sayHello +`関数は、https://www.digitalocean.com/community/tutorials/understanding-arrays-and-slices-in-go#slices [+ slice `]として ` names `パラメーターを受け取ります。 データ型はhttps://www.digitalocean.com/community/tutorials/understanding-data-types-in-go#strings [` string `]であるため、 ` names `パラメーターは次のように扱うことができます。関数本体内の文字列のスライス( ` [] string +`)。 https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-go#looping-through-sequential-data-types-with-rangeclause [`でループを作成できます。 + range + `]演算子を使用して、文字列のスライスを反復処理します。

プログラムを実行すると、次の出力が得られます。

OutputHello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

`+ sayHello `を初めて呼び出したときに何も出力されないことに注意してください。 これは、可変引数が空の ` slice `の ` string `であったためです。 スライスをループしているため、繰り返し処理するものはなく、 ` fmt.Printf +`が呼び出されることはありません。

プログラムを変更して、値が送信されなかったことを検出しましょう。

hello.go

package main

import "fmt"

func main() {
   sayHello()
   sayHello("Sammy")
   sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
   if len(names) == 0 {
       fmt.Println("nobody to greet")
       return
   }
   for _, n := range names {
       fmt.Printf("Hello %s\n", n)
   }
}

ここで、https://www.digitalocean.com/community/tutorials/how-to-write-conditional-statements-in-go#if-statements [`+ if `ステートメント]を使用して、値が渡されない場合、 ` names `の長さは ` 0 `になり、 ` nobody to greet +`を出力します:

Outputnobody to greet
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

可変引数を使用すると、コードが読みやすくなります。 指定された区切り文字で単語を結合する関数を作成しましょう。 最初に可変機能を持たないこのプログラムを作成して、その読み方を示します。

join.go

package main

import "fmt"

func main() {
   var line string

   line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
   fmt.Println(line)

   line = join(",", []string{"Sammy", "Jessica"})
   fmt.Println(line)

   line = join(",", []string{"Sammy"})
   fmt.Println(line)
}

func join(del string, values []string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}

このプログラムでは、コンマ( )を区切り文字として `+ join +`関数に渡します。 次に、結合する値のスライスを渡します。 これが出力です:

OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

関数は文字列のスライスを `+ values `パラメーターとして受け取るため、 ` join +`関数を呼び出すときにすべての単語をスライスにラップする必要がありました。 これにより、コードが読みにくくなる可能性があります。

ここで、同じ関数を記述しましょう。しかし、可変個の関数を使用します。

join.go

package main

import "fmt"

func main() {
   var line string

   line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
   fmt.Println(line)

   line = join(",", "Sammy", "Jessica")
   fmt.Println(line)

   line = join(",", "Sammy")
   fmt.Println(line)
}

func join(del string, values ...string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}

プログラムを実行すると、前のプログラムと同じ出力が得られることがわかります。

OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

`+ join +`関数の両方のバージョンはプログラム的にまったく同じことを行いますが、関数の可変長バージョンは呼び出されたときに読みやすくなります。

可変引数の順序

関数には1つの変数パラメータのみを含めることができ、それは関数で定義された最後のパラメータでなければなりません。 最後のパラメーター以外の任意の順序で変数関数のパラメーターを定義すると、コンパイルエラーが発生します。

join.go

package main

import "fmt"

func main() {
   var line string

   line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
   fmt.Println(line)

   line = join(",", "Sammy", "Jessica")
   fmt.Println(line)

   line = join(",", "Sammy")
   fmt.Println(line)
}

func join(values ...string, del string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}

今回は、 + join`関数で最初に + value as`パラメーターを設定します。 これにより、次のコンパイルエラーが発生します。

Output./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

可変引数関数を定義する場合、最後のパラメーターのみが可変引数になります。

爆発的な議論

これまでに、0、1、またはそれ以上の値を可変引数関数に渡すことができることを見てきました。 ただし、値のスライスがあり、変数の関数に送信したい場合があります。

最後のセクションの「+ join +」関数を見て、何が起こるか見てみましょう。

join.go

package main

import "fmt"

func main() {
   var line string



   line = join(",", )
   fmt.Println(line)
}

func join(del string, values ...string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}

このプログラムを実行すると、コンパイルエラーが表示されます。

Output./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

variadic関数は `+ values …​ string `のパラメーターを文字列のスライス ` [] string +`に変換しますが、文字列のスライスを引数として渡すことはできません。 これは、コンパイラが文字列の個別の引数を予期しているためです。

これを回避するには、スライスの接尾辞( + …​ +)を接尾辞としてスライスに_explode_して、可変個の関数に渡される個別の引数に変換します。

join.go

package main

import "fmt"

func main() {
   var line string

   names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

   line = join(",", )
   fmt.Println(line)
}

func join(del string, values ...string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}

今回は、 + join +`関数を呼び出したときに、楕円( `+ …​ +)を追加して `+ names +`スライスを分解しました。

これにより、プログラムが期待どおりに実行できるようになります。

OutputSammy,Jessica,Drew,Jamie

ゼロ、1つ、またはそれ以上の引数と爆発したスライスを渡すことができることに注意することが重要です。 これまで見てきたすべてのバリエーションを渡すコードを次に示します。

join.go

package main

import "fmt"

func main() {
   var line string

   line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
   fmt.Println(line)

   line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
   fmt.Println(line)

   line = join(",", "Sammy", "Jessica")
   fmt.Println(line)

   line = join(",", "Sammy")
   fmt.Println(line)

}

func join(del string, values ...string) string {
   var line string
   for i, v := range values {
       line = line + v
       if i != len(values)-1 {
           line = line + del
       }
   }
   return line
}
OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

これで、0、1、または多くの引数と、爆発するスライスを可変引数関数に渡す方法がわかりました。

結論

このチュートリアルでは、可変引数関数がコードをきれいにする方法を説明しました。 必ずしもそれらを使用する必要はありませんが、便利な場合があります。

  • 関数に渡すために一時的なスライスを作成していることがわかった場合。

  • 入力パラメーターの数が不明な場合、または呼び出されたときに変化する場合。

  • コードを読みやすくするため。

関数の作成と呼び出しの詳細については、https://www.digitalocean.com/community/tutorials/how-to-define-and-call-functions-in-go [Goで関数を定義して呼び出す方法]を参照してください。 ]。