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 可以,因为它有这个变量的内存地址的 引用。