Skip to content

Latest commit

 

History

History
128 lines (89 loc) · 5.18 KB

File metadata and controls

128 lines (89 loc) · 5.18 KB

函数

编程里的函数和数学里的函数不是一个概念。

函数是什么?其实你之前就已经接触过函数了,fmt.Println 中的 Println 就是函数。

大多数时候,我们写的代码不会只被用一次。比如前面的通过出生年和当前年算年龄的代码,以后还可能会用来算大明的年龄。重新粘贴代码显然是不明智的,我们能不能把复杂的代码片段保存起来,以后方便地重用呢?

当然可以,这就是函数。

fmt.Println 中的 Println 就是函数,把内容输出到屏幕上有着一系列复杂的操作。所以每个编程语言都会预定义一些函数给开发者使用,我们自己写代码的时候不需要关注语言怎么和硬件交互怎么把内容弄到屏幕上显示出来。我们只要知道有个 fmt.Println 函数,只要传进去内容,它就能做到把东西打印到屏幕上,就够了。

这就像你双击电脑上的图标,就能打开软件一样;或者说只要投个币,旋转小马就会转一样。把复杂的操作封装起来,给定的输出会触发相应的操作,同时还能得到相应的结果,这就是函数了。

下面看一段简单的代码:

func GetAge(birthYear int, nowYear int) (age int) {
  fmt.Println("传入函数的出生年是 %d", birthYear)
  fmt.Println("传入函数的当前年是 %d", nowYear)
  return nowYear - birthYear
}

func main() {
  birthYearOfXiaoMing, nowYearOfXiaoMing := 2006, 2022
  fmt.Printf("小明的年龄是:%d\n", GetAge(birthYearOfXiaoMing, nowYearOfXiaoMing))
}

可以看到我们定义了一个算年龄的函数,

  • 其中 func 是关键字,表明这是个函数;
  • birthYear int, nowYear int 是形式参数列表,简称「形参」
  • age int 是返回值,就是函数运算的结果。
fmt.Printf("小明的年龄是:%d\n", GetAge(birthYearOfXiaoMing, nowYearOfXiaoMing))

// 等同于

age := GetAge(birthYearOfXiaoMing, nowYearOfXiaoMing) 	// GetAge 有个返回值,赋给了 age
fmt.Printf("小明的年龄是:%d\n", age)
  • 大括号 {} 围起来的是函数体, 也就是我们封装起来的复杂的操作。

  • birthYearOfXiaoMing, nowYearOfXiaoMing 是实参,即我们实际传递给函数的数据。

调用的时候,直接用「函数名+实参列表」就行,如 GetAge(2006, 2022),示例中是传了两个变量,效果是一样的。

可以看到,实参和形参名字可以不一样。形参其实只是代表这里是个什么类型的什么值,调用的时候是传常量还是变量,甚至传个表达式都没有问题,因为他们最终的计算结果都是个值。

比如 GetAge(1+2+3, 3+4*5),又比如 fmt.Printf("小明的年龄是:%d\n", GetAge(birthYearOfXiaoMing, nowYearOfXiaoMing)) 这一行,其实就是把 GetAge(birthYearOfXiaoMing, nowYearOfXiaoMing) 这个函数调用作为一个整体传进了 fmt.Printf 里面,因为本质上 GetAge 的函数调用返回的就是一个值。

  • 所以再归纳一下,函数就是可以封装一系列复杂的操作(放到函数体里面),方便以后复用。
  • 同时,为了增加灵活性,可以有任意个输入的变量,即形参,这样每次调用都能做不同的操作。
  • 最后还有返回值,可以把运算的结果告诉调用者。

所以上面这三个就是函数的核心,其他的各种变体都是围绕这个展开的。

我们尝试变一下,还是以计算年龄这个函数为例:

func GetAge(birthYear int, nowYear int) int {
  return nowYear - birthYear
}

这段的变化是,返回值从 age int 变成了 int,即只留下了类型删去了变量名。因为最后调用即 age := GetAge(1, 2) 的时候得到的结果会赋给新的变量,所以调用方只要知道类型就行了,变量名会被重写。(建议:除非返回值非常清楚,否则建议带上变量名,增加可读性)

func GetAge(birthYear, nowYear int) int {
  return nowYear - birthYear
}

这里主要是参数列表中少了一个 int 类型,因为这两个形参是同一个类型,所以可以省略一个,和变量定义的时候同理。

func GetAge(birthYear int, nowYear int) (age int, ageNextYear int) {
  age := nowYear - birthYear
  return age, age+1
}

这里是返回值变成了两个。Go 支持任意多个返回值。

func GetAge(birthYear, nowYear int) {
  fmt.Printf("年龄是 %d", nowYear - birthYear)
}

既然支持任意个返回值,当然也可以没有返回值。

func DogSpeak() {
  fmt.Println("汪汪汪!")
}

既然可以没有返回值,那么形参列表也可以是空。

其实还有其他的骚操作,这里就不一一举例了。

最后再来个例子:

package main
import (
	"fmt"
	"time"
)

func GetAge(birthYear int) int {
  nowYear := time.Now().Year()
  return nowYear - birthYear
}

func main() {
  birth := 2006
  fmt.Printf("小明今年的年龄是:%d\n", GetAge(birth))
}

time.Now() 是 Go 自带的函数,可以获取当前的时间。继续 .Year() 就可以获取当前年,这样我们就获得了一个真正的获取年龄的好用的函数了!

(至于 .Year() 又是什么语法,我们后面再讲。)