序章

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

可変個引数関数は、見た目よりも一般的です。 最も一般的なものは Println fmtパッケージの関数。

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 次の値を使用して、1つの引数のみを渡します。 one. それから私たちは合格します onetwo、 そして最後に one, two、 と three.

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

  1. go run print.go

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

Output
one one two one two three

出力の最初の行は空白です。 これは、最初に引数を渡さなかったためです fmt.Println と呼ばれていました。 2回目の値 one 印刷されました。 それで onetwo、 そして最後に 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)
	}
}

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

The sayHello 関数は names スライスとしてのパラメータ。 データ型は文字列であるため、 names パラメータは文字列のスライスのように扱うことができます([]string)関数本体の内部。 range 演算子を使用してループを作成し、文字列のスライスを反復処理できます。

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

Output
Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

初めて電話したときに何も印刷されなかったことに注意してください sayHello. これは、可変個引数パラメータが空だったためです slicestring. スライスをループしているので、繰り返すものは何もありません。 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)
	}
}

ここで、 ifステートメントを使用することにより、値が渡されない場合、 names になります 0、印刷します nobody to greet:

Output
nobody 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 関数。 次に、結合する値のスライスを渡します。 出力は次のとおりです。

Output
Sammy,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
}

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

Output
Sammy,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
}

今回は values 最初のパラメータ join 関数。 これにより、次のコンパイルエラーが発生します。

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

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

	line = join(",", names)
	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

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

これを回避するために、楕円のセット(...)そしてそれを可変個引数関数に渡される離散引数に変換します:

join.go
package main

import "fmt"

func main() {
	var line string

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

	line = join(",", names...)
	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 省略記号を追加してスライスする(...).

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

Output
Sammy,Jessica,Drew,Jamie

分解するスライスだけでなく、0、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
}
Output
Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

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

結論

このチュートリアルでは、可変個引数関数によってコードがよりクリーンになる方法を見てきました。 常に使用する必要はありませんが、便利な場合があります。

  • 関数に渡すためだけに一時的なスライスを作成していることがわかった場合。
  • 入力パラメータの数が不明であるか、呼び出されたときに変化する場合。
  • コードを読みやすくするため。

関数の作成と呼び出しの詳細については、Goで関数を定義して呼び出す方法を参照してください。