package main import "fmt" // 这里是一个函数,接受两个 `int` 并且以 `int` 返回它们的和 func plus(a int, b int) int { // Go 需要明确的返回,不会自动返回最 // 后一个表达式的值 return a + b } // 当多个连续的参数为同样类型时,最多可以仅声明最后一个参数类型 // 而忽略之前相同类型参数的类型声明。 func plusPlus(a, b, c int) int { return a + b + c } func main() { // 通过 `name(args)` 来调用函数, res := plus(1, 2) fmt.Println("1+2 =", res) res = plusPlus(1, 2, 3) fmt.Println("1+2+3 =", res) }
Go 函数有很多其他的特性。其中一个就是多值返回
package main import "fmt" // `(int, int)` 在这个函数中标志着这个函数返回 2 个 `int`。 func vals() (int, int) { return 3, 7 } func main() { // 这里我们通过_多赋值_操作来使用这两个不同的返回值。 a, b := vals() fmt.Println(a) fmt.Println(b) // 如果你仅仅需要返回值的一部分的话,你可以使用空白标识符`_`。 _, c := vals() fmt.Println(c) }
可变参数函数。在调用时可以用任意数量的参数。 例如,fmt.Println 是一个常见的变参函数。
package main import "fmt" // 这个函数接受任意数目的 `int` 作为参数。 func sum(nums ...int) { fmt.Print(nums, " ") total := 0 for _, num := range nums { total += num } fmt.Println(total) } func main() { // 变参函数使用常规的调用方式,传入独立的参数。 sum(1, 2) sum(1, 2, 3) // 如果你有一个含有多个值的 slice,想把它们作为参数 // 使用,你要这样调用 `func(slice...)`。 nums := []int{1, 2, 3, 4} sum(nums...) }
Go 函数的另一个关键的方面是闭包结构
Go 支持匿名函数,并能用其构造 闭包。 匿名函数在你想定义一个不需要命名的内联函数时是很实用的。
package main import "fmt" // 这个 `intSeq` 函数返回另一个在 `intSeq` 函数体内定义的 // 匿名函数。这个返回的函数使用闭包的方式 _隐藏_ 变量 `i`。 func intSeq() func() int { i := 0 return func() int { i++ return i } } func main() { // 我们调用 `intSeq` 函数,将返回值(一个函数)赋给 // `nextInt`。这个函数的值包含了自己的值 `i`,这样在每 // 次调用 `nextInt` 时都会更新 `i` 的值。 nextInt := intSeq() // 通过多次调用 `nextInt` 来看看闭包的效果。 fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) // 为了确认这个状态对于这个特定的函数是唯一的,我们 // 重新创建并测试一下。 newInts := intSeq() fmt.Println(newInts()) }
Go 支持 递归。 这里是一个经典的阶乘示例
package main import "fmt" // `fact` 函数在到达 `fact(0)` 前一直调用自身。 func fact(n int) int { if n == 0 { return 1 } return n * fact(n-1) } func main() { fmt.Println(fact(7)) }
Go 支持 指针, 允许在程序中通过引用传递值或者数据结构。
package main import "fmt" // 我们将通过两个函数:`zeroval` 和 `zeroptr` 来比较指针和 // 值类型的不同。`zeroval` 有一个 `int` 型参数,所以使用值 // 传递。`zeroval` 将从调用它的那个函数中得到一个 `ival` // 形参的拷贝。 func zeroval(ival int) { ival = 0 } // `zeroptr` 有一和上面不同的 `*int` 参数,意味着它用了一 // 个 `int`指针。函数体内的 `*iptr` 接着_解引用_这个指针, // 从它内存地址得到这个地址对应的当前值。对一个解引用的指 // 针赋值将会改变这个指针引用的真实地址的值。 func zeroptr(iptr *int) { *iptr = 0 } func main() { i := 1 fmt.Println("initial:", i) zeroval(i) fmt.Println("zeroval:", i) // 通过 `&i` 语法来取得 `i` 的内存地址,即指向 `i` 的指针。 zeroptr(&i) fmt.Println("zeroptr:", i) // 指针也是可以被打印的。 fmt.Println("pointer:", &i) }
zeroval
在 main
函数中不能改变 i
的值,但是 zeroptr
可以,因为它有这个变量的内存地址的 引用。