zhcn 编程语言 Golang Golang 容器 非公開: 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 行,输入三个字符串并使用 joinStrings() 函数将参数连接到字符串输出。
  • 第25行输入4个字符的字符串,连接后输出。

如果想获取可变参数的个数,可以使用len()函数计算可变参数的变量对应的切片长度,得到可变参数的个数。

get variadic type – 获取每个参数的类型

如果变量参数的类型为interface{},则可以传递任何类型的值。这时如果需要获取变量的类型,可以通过switch来获取变量的类型。以下代码将一系列不同类型的值传递给 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 的每个元素。类型是接口{}。
  • 第 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 并打印它们。

如果我尝试将第 20 行更改为:

 rawPrint("fmt", slist) 

再次运行代码将输出以下内容:

[1 2 3]

此时,slist(type []interface{})整体传递给rawPrint(),rawPrint()函数内部传递的变量也成为slist的切片值。

使用...传递的变量参数与使用切片之间的附加连接具有相同的功能。

《Go语言中的变量参数(变量函数)》浅显易懂的讲解!您必须观看的最佳 2 个视频

一緒に学ぶGo言語入門 ~その2:パッケージ・変数・関数(前半)~
https://www.youtube.com/watch?v=2aiTzfvEWKg&pp=ygU5IEdv6KiA6Kqe44Gu5aSJ5pWw44OR44Op44Oh44O844K_77yI5aSJ5pWw6Zai5pWw77yJJmhsPUpB
Go言語とは?|プログラミング言語のGo言語について3分でわかりやすく解説します【プログラミング初心者向け】
https://www.youtube.com/watch?v=3zfzBuDl6M4&pp=ygU5IEdv6KiA6Kqe44Gu5aSJ5pWw44OR44Op44Oh44O844K_77yI5aSJ5pWw6Zai5pWw77yJJmhsPUpB
在 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 行,输入三个字符串并使用 joinStrings() 函数将参数连接到字符串输出。
  • 第25行输入4个字符的字符串,连接后输出。

如果想获取可变参数的个数,可以使用len()函数计算可变参数的变量对应的切片长度,得到可变参数的个数。

get variadic type – 获取每个参数的类型

如果变量参数的类型为interface{},则可以传递任何类型的值。这时如果需要获取变量的类型,可以通过switch来获取变量的类型。以下代码将一系列不同类型的值传递给 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 的每个元素。类型是接口{}。
  • 第 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 并打印它们。

如果我尝试将第 20 行更改为:

 rawPrint("fmt", slist) 

再次运行代码将输出以下内容:

[1 2 3]

此时,slist(type []interface{})整体传递给rawPrint(),rawPrint()函数内部传递的变量也成为slist的切片值。

使用...传递的变量参数与使用切片之间的附加连接具有相同的功能。

《Go语言中的变量参数(变量函数)》浅显易懂的讲解!您必须观看的最佳 2 个视频

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