函数
什么是函数?
函数是执行特定任务的代码块。函数接受输入,对输入执行一些计算,然后生成输出。
通常每一个程序都包含有很多的函数,系统通过函数来划分不同功能,将整体任务进行分解。
在 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)
}
上面的程序会打印
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)
}
如果函数返回多个返回值,则必须在(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)
}
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() {
}
运行上述程序将输出 。我们已经成功初始化了包,即使它没有在代码中的任何位置使用。