Go语言可变参数函数

什么是可变参数函数?

函数通常只接受固定数量的参数。可变参数函数是接受可变数量参数的函数。如果函数定义的最后一个参数以省略号*…*为前缀,则该函数可以接受该参数的任意数量的参数。

只有函数的最后一个参数可以是可变参数。我们将在本教程的下一部分中了解为什么会出现这种情况。

语法

func hello(a int, b ...int) {  
}

在上面的函数中,参数b是可变参数,因为它以省略号为前缀,并且可以接受任意数量的参数。可以使用语法调用该函数。

hello(1, 2) 
hello(5, 6, 7, 8, 9) 

在上面的代码中, 我们调用hello函数数字1作为第一个参数传递给a,数字2作为第二个参数传递给b

并在下一行中b传递四个参数6, 7, 8, 9

也可以将不传递参数给可变参数函数。

hello(1)  

在上面的代码中,我们hello使用零参数调用b. 这完全没问题。

现在我想你应该明白为什么可变参数只能放在最后。

让我们尝试将hello函数的第一个参数设为可变参数。

语法看起来像

func hello(b ...int, a int) {  
}

在上面的函数中,不可能将参数传递给a参数,因为我们传递的任何参数都将被分配给第一个b参数,因为它是可变参数。

因此,可变参数只能出现在函数定义的最后。上面的函数将无法编译并出现错误

syntax error: cannot use ... with non-final parameter b

示例和理解可变参数函数如何工作

让我们创建自己的可变参数函数。我们将编写一个简单的程序来查找整数输入列表中是否存在整数。

package main

import (  
    "fmt"
)

func find(num int, nums ...int) {  
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
        if v == num {
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {  
    find(89, 89, 90, 95)
    find(45, 56, 67, 45, 90, 109)
    find(78, 38, 56, 98)
    find(87)
}

Run in Playground

在上面的程序中,在函数func find(num int, nums ...int)参数接受可变数量的参数nums类型是 []int整数切片。

可变参数函数的工作方式是将可变数量的参数转换为可变参数类型的切片。

例如,在上面程序中,find函数的可变参数数量值为 89、90、95。find 函数需要一个可变int参数。因此,这三个参数将被编译器转换为 int 类型的切片[]int{89, 90, 95},然后将其传递给find函数。

上述程序输出,

type of nums is []int  
89 found at index 0 in [89 90 95]

type of nums is []int  
45 found at index 2 in [56 67 45 90 109]

type of nums is []int  
78 not found in  [38 56 98]

type of nums is []int  
87 not found in  []  

在上面的程序中,最后一次调用find 函数调用只有一个参数。我们没有向可变nums ...int参数传递任何参数。

如前所述,这是完全合法的,在这种情况下,nums将是nil长度和容量为 0 的切片。

切片参数与可变参数

我们现在肯定有一个问题萦绕在您的脑海中。在上一节中,我们了解到函数的可变参数实际上被转换为切片。那么当我们可以使用切片实现相同的功能时,为什么我们还需要可变参数函数呢?我已经使用切片重写了上面的程序。

package main

import (  
    "fmt"
)

func find(num int, nums []int) {  
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
        if v == num {
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {  
    find(89, []int{89, 90, 95})
    find(45, []int{56, 67, 45, 90, 109})
    find(78, []int{38, 56, 98})
    find(87, []int{})
}

Run in Playground

以下是使用可变参数而不是切片的优点。

  1. 无需在每次函数调用期间创建切片。如果您查看上面的程序,我们会在每次函数调用期间创建新的切片。使用可变参数函数时可以避免这种额外的切片创建
  2. 在上面程序的中最后一次调用find,我们创建一个空切片只是为了满足函数的签名find。对于可变参数函数来说,这是完全不需要的。find(87)仅当使用可变参数函数时才 可以使用此行。
  3. 我个人认为带有可变参数函数的程序比带有切片的程序更具可读性

Append 是一个可变参数函数

您是否想知道标准库中的Append函数如何用于将值附加到切片接受任意数量的参数。这是因为它是一个可变函数。

func append(slice []Type, elems ...Type) []Type  

以上是append函数的定义。在这个定义中elems是一个可变参数。因此,append 可以接受可变数量的参数。

将切片传递给可变参数函数

让我们将切片传递给可变参数函数,并从下面的示例中了解会发生什么。

package main

import (  
    "fmt"
)

func find(num int, nums ...int) {  
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
        if v == num {
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {  
    nums := []int{89, 90, 95}
    find(89, nums)
}

Run in Playground

在上述程序中,我们将一个切片传递给一个需要可变数量参数的函数。

这是行不通的。上述程序将因编译错误而失败./prog.go:23:10: cannot use nums (type []int) as type int in argument to find

为什么这不起作用?嗯,这很简单。find下面提供了函数的签名,

func find(num int, nums ...int)  

根据可变参数函数的定义,nums ...int意味着它将接受可变数量的类型参数int

上面程序中,nums即 []int slice 被传递给find需要可变int参数的函数。正如我们已经讨论过的,这些可变参数将被转换为类型切片,int因为find需要可变参数 int 参数。在这种情况下,nums已经是一个[]int切片,编译器尝试创建一个新的切片[]int,即编译器尝试执行的操作

find(89, []int{nums})  

这将会失败,因为numsis a[]int而不是 a int

那么有没有办法将切片传递给可变参数函数呢?答案是肯定的。

有一个语法糖可用于将切片传递给可变参数函数。您必须在切片后加上省略号。...如果这样做,切片将直接传递给函数,而不会创建新切片。

在上面的程序中,如果您将上面的程序替换成find(89, nums...)

package main

import (  
    "fmt"
)

func find(num int, nums ...int) {  
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
        if v == num {
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {  
    nums := []int{89, 90, 95}
    find(89, nums...)
}

Run in Playground

上面程序输出

type of nums is []int
89 found at index 0 in [89 90 95]

问题

当您修改可变参数函数内的切片时,请确保您知道自己在做什么。

让我们看一个简单的例子。

package main

import (  
    "fmt"
)

func change(s ...string) {  
    s[0] = "Go"
}

func main() {  
    welcome := []string{"hello", "world"}
    change(welcome...)
    fmt.Println(welcome)
}

Run in Playground

你认为上述程序的输出会是什么?如果您认为这会输出[Go world]`那么恭喜您!您已经了解了可变参数函数和切片。如果你错了,也没什么大不了的,让我解释一下我们是如何得到这个输出的。

我们使用语法糖...并将切片作为可变参数传递给函数change

正如我们已经讨论过的,如果...使用,welcome切片本身将作为参数传递,而不创建新切片。因此welcome将作为参数传递给change函数。

在更改函数内,切片的第一个元素更改为Go。因此这个程序输出

[Go world]

这是另一个理解可变参数函数的程序。

package main

import (  
    "fmt"
)

func change(s ...string) {  
    s[0] = "Go"
    s = append(s, "playground")
    fmt.Println(s)
}

func main() {  
    welcome := []string{"hello", "world"}
    change(welcome...)
    fmt.Println(welcome)
}

Run in Playground

我将把它作为练习,让您弄清楚上述程序是如何工作的

[Go world playground]
[Go world]

你可能感兴趣的:(Go语言教程,golang,算法,开发语言)