Go语言的函数

2022-10-20 ⏳3.1分钟(1.2千字) g

本文是Go语言入门教程的一部分。我认为函数是Go语言的基础单位。传统教材都是先讲 Hello World,这个时候有意回避函数的概念。紧接着就转到介绍类型系统。我认为可以直接从函数入手,因为Go程序本身就是一个函数(main),这样从逻辑上会更清晰一些。但因为没有类型系统的知识,所以只能强行跟读者假定int或string等基础类型。这是先讲函数的不足。现在开始正文内容。

Go语言是一种过程式语言,一个过程也叫一个函数。函数可能包含名字、入参和反回值三个部分。之所以说可能是因为每一个部分都不是必须的。举一个简单的例子:

func swap(a int, b int) (int, int) {
  return b, a
}

函数以关键字func开头,这个不能省略。swap表示函数名。后面括号里的部分a int, b int表示函数的入参列表。a和b表示参数名,int表示参数的类型。我们会在下一节详细讨论 Go 的类型系统。现在大家只需要记住int表示整数类型,a和b可以传入数字就可以了。函数可以有多个参数,也可以没有参数,但每个参数都应该有名字,也要指定类型。括号后面还有一对括号,表示函数的返回值列表。Go 语言的函数支持返回多个参数,这算是一个特色。返回值列表的结构跟入参列表一样,但可以不指定参数名。最后的{}之间的部分就是函数过程。函数过程中可能包含多个return表示函数执行结束。return之后需要指定返回值,类型和数量都要跟返回值列表保持统一。

如果我们执行swap(1,2),函数会返回2和1两个值。

我们前面说过,函数的每一个部分都不是必须的。所以最简单的函数是func(){}。它没有名字,没有入参,没有返回值,也没有函数过程。但它是一个合法的函数😂

函数之间可以相互调用,比如:

func add(a int, b int) int { return a + b }
func sub(a int, b int) int { return a - b }
func mul(a int, b int) int { return a * b }

// 计算 (a+b)*(a-b)
func foo(a int, b int) int {
  return mul(add(a, b), sub(a, b))
}

其实以上代码都不能运行,因为 Go 语言要求每一个函数名都需要有一个包名(package)。而包名需要在源文件的开头指定:

package foo // 指定包名为 foo

func add(a int, b int) int { return a + b }

Go 实际调用函数的时候都是用「全名」,也就是包名+函数名的形式。比如上面的add全名是foo.add。一个 Go 程序其实就是一连串相互调用的函数。那程序在运行的时候需要从哪个函数开始呢?答案是main.main函数。也就是说,操作系统会直接运行main.main函数,我们可以在main.main函数中调用其他各种函数并等待返回结果。所以典型的函数调用流程如下:

os -> main.main() +-> a.foo() --> b.bar()
                  |-> c.baz()
		  +-> d.zoo() +-> x.cos()
		              |-> y.sin()

main.main()比较特殊,由操作系统直接调用,没有入参,也没有返回值。所以最小且可以执行的 Go 程序是:

package main

func main() {}

但这个程序什么功能也没有,操作系统执行main.main()函数后马上就退出了。

再复杂一点的程序就是著名的 Hello World 程序:

package main

import "fmt"

func main() {
  fmt.Println("Hello, World!")
}

我们前面讲每一个函数都需要定义包名。包名相同的函数可以直接使用函数名相互调用,比如前面的foo和add。但包名不相同的则必须使用全名调用。此外,如果要调用其他包名,还需要通过import来导入对应的包。比如这里的import "fmt",就是导入名为fmt的包。这是 Go 提供的标准包,用于格式化输出。

fmt.Println() 表示向标准输出设备(stdout)输出一行字符串,也就是Hello, World!。我们把上面的代码保存到hello.go文件,然后执行:

$ go run hello.go
Hello, World!

除了使用标准包外,我们还可以定义自己的包。但这部分内容涉及到包结构体系和工程化设计,并非现阶段的主要矛盾。等介绍完 Go 语言的核心知识后,我会再详细介绍包体系相关内容。接下来我们开始学习Go语言的类型系统。