函数
什么是函数?
函数是执行特定任务的代码块。函数接受输入,对输入执行一些计算,然后生成输出。
通常每一个程序都包含有很多的函数,系统通过函数来划分不同功能,将整体任务进行分解。
在 Go 语言中,函数的基本组成包括:关键字 func、函数名、参数列表、返回值、函数体和返回语句。
Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。
函数声明
在 go 中声明函数的语法是
func functionname(parametername type) returntype {
//function body
}
函数定义解析:
- func:函数由 func 开始声明。
- functionname:函数名称,函数名和参数列表一起构成了函数签名。
- functionname type:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- returntype:返回类型,函数返回一列值。有些功能不需要返回值,这种情况下 returntype不是必须的。
- 函数体:函数定义的代码集合。
示例函数
让我们编写一个函数,该函数将单个产品的价格和产品数量作为输入参数,并通过将这两个值相乘来计算总价格并返回输出。
func calculateBill(price int, no int) int {
var totalPrice = price * no
return totalPrice
}
上面的函数有两个 int 类型的输入参数price
,no
它返回的totalPrice
是价格和编号的乘积。返回值也是int类型。
如果连续的参数类型相同,就可以避免每次都写类型,最后写一次就可以了。即 price int, no int
可以写为price, no int
. 因此,上述函数可以重写为:
func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}
现在我们已经准备好了一个函数,让我们从代码中的某个地方调用它。调用函数的语法是functionname(parameters)
。可以使用代码调用上述函数。
calculateBill(10, 5)
这是使用上述函数并打印总价的完整程序。
package main
import (
"fmt"
)
func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}
func main() {
price, no := 90, 6
totalPrice := calculateBill(price, no)
fmt.Println("Total price is", totalPrice)
}
Run in playground
上面的程序会打印
Total price is 540
多个返回值
一个函数可以返回多个值。让我们编写一个函数rectProps
,它接受矩形的 length
和 width
并返回
长方形的面积->长和宽的乘积
周长->长和宽之和的两倍
package main
import (
"fmt"
)
func rectProps(length, width float64)(float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, perimeter := rectProps(10.8, 5.6)
fmt.Printf("Area %f Perimeter %f", area, perimeter)
}
Run in playground
如果函数返回多个返回值,则必须在(length
和width
它们之间指定)
。func rectProps(length, width float64)(float64, float64)
有两个 float64 参数length and width
并且还返回两个float64
值。上面的程序打印出
Area 60.480000 Perimeter 32.800000
命名返回值
可以从函数返回命名值。如果已命名返回值,则可以将其视为在函数的第一行中声明为变量。
上面的 rectProps 可以使用命名的返回值重写为
func rectProps(length, width float64)(area, perimeter float64) {
area = length * width
perimeter = (length + width) * 2
//无显式返回值
return
}
面积和周长是上述函数中的命名返回值。请注意,函数中的 return 语句不会显式返回任何值。由于 和 在函数声明中指定为返回值,因此在遇到 return 语句时会自动从函数返回它们。area
和perimeter
空白标识符
**_**在 Go 中称为空白标识符。它可以代替任何类型的任何值。让我们看看这个空白标识符有什么用。
该函数返回矩形的面积和周长。如果我们只需要 面积并想丢弃周长.这就是用处。area
和_
参考下面的程序
package main
import (
"fmt"
)
func rectProps(length, width float64) (float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, _ := rectProps(10.8, 5.6) // perimeter is discarded
fmt.Printf("Area %f ", area)
}
Run in playground
Area 60.480000
什么是包,为什么使用它们?
到目前为止,我们已经看到 Go 程序只有一个包含一个 main函数和几个其他函数的文件。在现实场景中,这种将所有源代码写入单个文件的方法是不可扩展的。重用和维护以这种方式编写的代码变得不可能。这就是包的用处。
包用于组织 Go 源代码,以提高可重用性和可读性。包是驻留在同一目录中的 Go 源文件的集合。包提供了代码划分,因此维护 Go 项目变得很容易。
演示图例
Go 模块
Go 模块只不过是 Go 包的集合。现在你可能会想到这个问题。为什么我们需要 Go 模块来创建自定义包?答案是我们创建的自定义包的导入路径派生自 go 模块的名称。除此之外,我们的应用程序使用的所有其他第三方软件包(例如来自 github 的源代码)将与版本一起出现在文件中。此文件是在我们创建新模块时创建的。您将在下一节中更好地理解这一点。
cd learnpackage 目录下
//执行
go mod init learnpackage
上面的命令将创建一个名为go.mod
的文件,以下是文件的内容。
module learnpackage
go 1.21.0
我们创建simpleinterest文件夹,在创建一个simpleinterest.go
文件
package simpleinterest
//计算并返回本金p的单利,利率r的时间跨度为t年
func Calculate(p float64, r float64, t float64) float64 {
interest := p * (r / 100) * t
return interest
}
在上面的代码中,我们创建了一个计算并返回简单利息的函数Calculate。此功能是计算并返回单利。
请注意,函数名称 Compute 以大写字母开头。这是必不可少的
导入自定义包
要使用自定义包,我们必须先导入它。导入路径是附加在包的子目录和包名称上的模块名称。在我们的例子中,模块名称是learnpackage,包在直接位于下面的文件夹中的simpleinterest
目前项目目录:
导入simpleinterest
上面的代码导入包并使用函数查找单利。标准库中的包不需要模块名称前缀,因此“fmt”在没有模块前缀的情况下工作。当应用程序运行时,输出将是`
Simple interest calculation
Simple interest is 500
初始化函数
Go 中的每个包都可以包含一个函数。该函数不得具有任何返回类型,并且不得具有任何参数。init 函数不能在我们的源代码中显式调用。初始化包时将自动调用它。初始化函数具有以下语法init``init
func init() {
}
该函数可用于执行初始化任务,也可用于在执行开始之前验证程序的正确性。init
包的初始化顺序如下
- 首先初始化包级变量
- 接下来调用初始化函数。包可以有多个 init 函数(在单个文件中或分布在多个文件中),并且按照它们呈现给编译器的顺序调用它们。
如果包导入其他包,则首先初始化导入的包。
即使从多个包导入包,也只会初始化一次包。
让我们对应用程序进行一些修改以了解函数。init
首先,让我们将函数添加到文件中。init``simpleinterest.go
package simpleinterest
import "fmt"
/*
* 新增Init功能
*/
func init() {
fmt.Println("初始化 simpleinterest")
}
// 计算并返回本金p的单利,利率r的时间跨度为t年interest r for time duration t years
func Calculate(p float64, r float64, t float64) float64 {
interest := p * (r / 100) * t
return interest
}
我们添加了一个简单的初始化函数
现在让我们修改主包。我们知道,在计算单利时,本金、利率和持续时间应大于零。我们将在文件中使用 init 函数和包级变量来定义此检查。main.go
将 修改为以下内容,main.go
package main
import (
"fmt"
"learnpackage/simpleinterest"
)
func init() {
fmt.Println("初始化 main")
}
var p, r, t = 5000.0, 10.0, 1.0
func main() {
fmt.Println("简单利息计算")
si := simpleinterest.Calculate(p, r, t)
fmt.Println("单利是", si)
}
以下是对main.go
- P、R 和 T 变量从主函数级别移动到包级别。
- 添加了初始化函数。init 函数使用 log 打印日志并在本金、利率或持续时间小于零时终止程序执行**。致命**功能。
初始化的顺序如下:
- 首先初始化导入的包。因此,首先初始化 simpleinterest 包,并调用它的 init 方法。
- 接下来初始化包级变量 p、r 和 t。
- 初始化函数在 main 中调用。
- 最后调用主函数。
如果运行该程序,将获得以下输出。
初始化 simpleinterest
初始化 main
简单利息计算
单利是 500
使用空白标识符
在 Go 中导入包而不在代码中的任何位置使用它都是非法的。如果您这样做,编译器将报错。这样做的原因是为了避免未使用的包膨胀,这将显着增加编译时间。将 main.go
中的代码替换为以下内容,
package main
import (
"learnpackage/simpleinterest"
)
func main() {
}
上述程序将出错
# learnpackage
./main.go:4:2: imported and not used: "learnpackage/simpleinterest"
但是,在应用程序处于积极开发状态时导入包并在以后(如果不是现在)在代码中的某个位置使用它们是很常见的。在这些情况下,空白标识符可以拯救我们。_
package main
import (
_ "learnpackage/simpleinterest"
)
func main() {
}
运行上述程序将输出 。我们已经成功初始化了包,即使它没有在代码中的任何位置使用。
上一教程 Go语言常量介绍