介绍
函数是一段代码,一旦定义,就可以重用。 函数用于通过将代码分解为可在整个程序中多次使用的小型可理解任务来使代码更易于理解。
随附具有许多预定义功能的强大标准库。 你可能已经从fmt包中熟悉的那些是:
-
fmt.Println()
将对象打印到标准输出(很可能是您的终端)。 -
fmt.Printf()
允许您格式化打印输出。
函数名称包括括号,可以包含参数。
在本教程中,我们将讨论如何定义自己的函数以在编码项目中使用。
定义一个函数
让我们首先将经典的“Hello,World!”程序转换为函数。
我们将在我们选择的文本编辑器中创建一个新的文本文件,并调用程序hello.go
。 然后,我们将定义函数。
使用func
关键字定义func
。 然后是您选择的名称和一组括号,其中包含该函数将采用的任何参数(它们可以为空)。 函数代码行用大括号{}
括起来。
在这种情况下,我们将定义一个名为hello()
的函数:
func hello() {}
这将设置创建函数的初始语句。
从这里开始,我们将添加第二行来提供函数功能的说明。 在这种情况下,我们将打印Hello, World!
到控制台:
func hello() { fmt.Println("Hello, World!")}
我们的函数现已完全定义,但如果我们此时运行程序,由于我们没有调用该函数,所以不会发生任何事情。
因此,在main()
函数块内部,让我们用hello()
调用该函数:
package mainimport "fmt"func main() { hello()}func hello() { fmt.Println("Hello, World!")}
现在,让我们运行程序:
go run hello.go
您将收到以下输出:
Hello, World!
请注意,我们还引入了一个名为main()
的函数。 main()
函数是一个特殊函数,它告诉编译器这是程序应该从哪里开始的 。 对于任何您想要执行的程序 (可以从命令行运行的程序),您将需要一个main()
函数。 main()
函数必须只出现一次,在main()
包中 ,并且不接收和返回任何参数。 这允许在任何Go程序中执行程序。 根据以下示例:
package mainimport "fmt"func main() { fmt.Println("this is the main section of the program")}
函数可能比我们定义的hello()
函数更复杂。 我们可以在功能块中使用for
循环 , 条件语句等。
例如,以下函数使用条件语句来检查name
变量的输入是否包含元音,然后使用for
循环迭代name
字符串中的字母。
package mainimport ( "fmt" "strings")func main() { names()}func names() { fmt.Println("Enter your name:") var name string fmt.Scanln(&name) // Check whether name has a vowel for _, v := range strings.ToLower(name) { if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' { fmt.Println("Your name contains a vowel.") return } } fmt.Println("Your name does not contain a vowel.")}
我们在这里定义的names()
函数用输入设置一个name
变量,然后在for
循环中设置一个条件语句。 这显示了如何在函数定义中组织代码。 但是,根据我们对程序的意图以及我们如何设置代码,我们可能希望将条件语句和for
循环定义为两个单独的函数。
在程序中定义函数使我们的代码模块化和可重用,这样我们就可以调用相同的函数而无需重写它们。
使用参数
到目前为止,我们已经查看了带有空括号的函数,这些函数不带参数,但我们可以在括号内的函数定义中定义参数。
参数是函数定义中的命名实体,指定函数可以接受的参数。 在Go中,您必须为每个参数指定数据类型 。
让我们创建一个程序,重复指定次数的单词。 它将采用一个名为word
的string
参数和一个名为reps
的int
参数作为重复该单词的次数。
package mainimport "fmt"func main() { repeat("Sammy", 5)}func repeat(word string, reps int) { for i := 0; i < reps; i++ { fmt.Print(word) }}
我们为值参数传递了值Sammy
,为reps
参数传递了5
。 这些值按照给定顺序与每个参数对应。 repeat
函数有一个for
循环,它将迭代reps
参数指定的reps
。 对于每次迭代,都会打印word
参数的值。
这是程序的输出:
SammySammySammySammySammy
如果您有一组具有相同值的参数,则可以省略每次指定的类型。 让我们创建一个小程序,它接受所有int
值的参数x
, y
和z
。 我们将创建一个函数,将参数添加到不同的配置中。 这些的总和将由函数打印。 然后我们将调用该函数并将数字传递给函数。
package mainimport "fmt"func main() { addNumbers(1, 2, 3)}func addNumbers(x, y, z int) { a := x + y b := x + z c := y + z fmt.Println(a, b, c)}
当我们为addNumbers
创建函数签名时,我们不需要每次都指定类型,而只是在结尾处。
我们为x
参数传递了数字1
,为y
参数传递了2
,为z
参数传递了3
。 这些值按照给定顺序与每个参数对应。
程序根据我们传递给参数的值进行以下数学运算:
a = 1 + 2b = 1 + 3c = 2 + 3
该函数还打印a
, b
和c
,基于此数学,我们期望a
等于3
, b
为4
, c
为5
。 让我们运行程序:
go run add_numbers.go
3 4 5
当我们将1
和3
作为参数传递给addNumbers()
函数时,我们会收到预期的输出。
参数是通常在函数定义中定义为变量的参数。 运行方法时,可以为它们分配值,并将参数传递给函数。
回归价值
您可以将参数值传递给函数,函数也可以生成值。
函数可以使用return
语句生成一个值,该语句将退出函数并可选择将表达式传递回调用者。 还必须指定返回数据类型。
到目前为止,我们在函数中使用了fmt.Println()
语句而不是return
语句。 让我们创建一个程序,而不是打印将返回一个变量。
在一个名为double.go
的新文本文件中,我们将创建一个程序,将参数x
加倍并返回变量y
。 我们发出一个调用来打印result
变量,该变量是通过运行double()
函数形成的,其中传入了3
:
package mainimport "fmt"func main() { result := double(3) fmt.Println(result)}func double(x int) int { y := x * 2 return y}
我们可以运行程序并查看输出:
go run double.go
6
整数6
作为输出返回,这是我们所期望的乘以3
乘以2
。
如果函数指定了返回,则必须提供返回作为代码的一部分。 如果不这样做,您将收到编译错误。
我们可以通过使用return语句注释掉这一行来证明这一点:
package mainimport "fmt"func main() { result := double(3) fmt.Println(result)}func double(x int) int { y := x * 2 // return y}
现在,让我们再次运行程序:
go run double.go
./double.go:13:1: missing return at end of function
这里不使用return
语句,程序无法编译。
函数在return
语句时立即退出,即使它们不在函数末尾:
package mainimport "fmt"func main() { loopFive()}func loopFive() { for i := 0; i < 25; i++ { fmt.Print(i) if i == 5 { // Stop function at i == 5 return } } fmt.Println("This line will not execute.")}
在这里,我们遍历for
循环,并告诉循环运行25
次迭代。 但是,在for
循环中,我们有一个条件if
语句,用于检查i
的值是否等于5
。 如果是,我们发出一个return
语句。 因为我们在loopFive
函数中, loopFive
函数中任何点的任何return
都将退出该函数。 因此,我们永远不会到达此函数的最后一行来打印语句。 This line will not execute.
。
在for
循环中使用return
语句会结束该函数,因此循环外部的行将不会运行。 相反,如果我们使用了break
语句 ,那么只有循环会在那时退出,并且最后一个fmt.Println()
行会运行。
return
语句退出函数,如果在函数签名中指定,则可以返回一个值。
返回多个值
可以为函数指定多个返回值。 让我们检查repeat.go
程序并使其返回两个值。 第一个是重复值,如果reps
参数不是大于0
值,则第二个将是错误:
package mainimport "fmt"func main() { val, err := repeat("Sammy", -1) if err != nil { fmt.Println(err) return } fmt.Println(val)}func repeat(word string, reps int) (string, error) { if reps <= 0 { return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps) } var value string for i := 0; i < reps; i++ { value = value + word } return value, nil}
repeat
函数的第一件事是检查reps
参数是否是有效值。 任何不大于0
值都将导致错误。 由于我们传入的值为-1
,因此将执行此代码分支。 请注意,当我们从函数返回时,我们必须提供string
和error
返回值。 因为提供的参数导致错误,我们将为第一个返回值传回一个空字符串,并为第二个返回值传递错误。
在main()
函数中,我们可以通过声明两个新变量value
和err
来接收两个返回值。 因为返回中可能存在错误,我们想在继续我们的程序之前检查是否收到错误。 在这个例子中,我们确实收到了一个错误。 我们打印出错误并return
main()
函数退出程序。
如果没有错误,我们将打印出函数的返回值。
注意:最佳做法是仅返回两个或三个值。 此外,您应始终将所有错误作为函数的最后一个返回值返回。
运行该程序将导致以下输出:
invalid value of -1 provided for reps. value must be greater than 0.
在本节中,我们回顾了如何使用return
语句从函数返回多个值。
结论
函数是在程序中执行操作的指令代码块,有助于使代码可重用和模块化。
要了解有关如何使代码更加模块化的更多信息,请阅读我们关于如何在Go中编写包的指南。