プログラミング言語 golang golang func Go言語の変数パラメータ(変数関数)

Go言語の変数パラメータ(変数関数)

 
 
C言語の時代にはprintf()関数を誰もが使うのが一般的でしたが、以来、C言語のprintf()関数やfmt.Println()などと同様に、可変パラメータの魅力と価値を実感してきました。実装は言語の可変個引数機能にも依存します。

 

このセクションでは、変数パラメーターの使用法を紹介します。変数パラメーターを適切に使用すると、特にログ関数などの入出力関数のコードがシンプルで使いやすくなります。

可変個引数型

可変パラメーターとは、関数によって渡されるパラメーターの数が可変であることを意味します。これを行うには、まず関数を可変パラメーターを受け入れることができる型として定義する必要があります。

 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 のスライス値になります。

...を使用して渡される変数パラメータは、スライス間の追加接続を使用する場合と同じ機能です。

 

「 Go言語の変数パラメータ(変数関数)」についてわかりやすく解説!絶対に観るべきベスト2動画

一緒に学ぶGo言語入門 ~その2:パッケージ・変数・関数(前半)~
Go言語とは?|プログラミング言語のGo言語について3分でわかりやすく解説します【プログラミング初心者向け】
 
 
C言語の時代にはprintf()関数を誰もが使うのが一般的でしたが、以来、C言語のprintf()関数やfmt.Println()などと同様に、可変パラメータの魅力と価値を実感してきました。実装は言語の可変個引数機能にも依存します。

 

このセクションでは、変数パラメーターの使用法を紹介します。変数パラメーターを適切に使用すると、特にログ関数などの入出力関数のコードがシンプルで使いやすくなります。

可変個引数型

可変パラメーターとは、関数によって渡されるパラメーターの数が可変であることを意味します。これを行うには、まず関数を可変パラメーターを受け入れることができる型として定義する必要があります。

 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 のスライス値になります。

...を使用して渡される変数パラメータは、スライス間の追加接続を使用する場合と同じ機能です。

 

「 Go言語の変数パラメータ(変数関数)」についてわかりやすく解説!絶対に観るべきベスト2動画

一緒に学ぶGo言語入門 ~その2:パッケージ・変数・関数(前半)~
Go言語とは?|プログラミング言語のGo言語について3分でわかりやすく解説します【プログラミング初心者向け】