• 在 Go 里,函数是头等的,它可以用在整数、字符串或其它类型能用的地方:
    • 将函数赋给变量
    • 将函数作为参数传递给函数
    • 将函数作为函数的返回类型

将函数赋给变量

  • 变量 sensor 就是一个函数,而不是函数执行的结果
  • 无论 sensor 的值是 fakeSensor 还是 realSensor,都可以通过 sensor() 来调用
  • sensor 这个变量的类型是函数,该函数没有参数,返回一个 kelvin 类型的值。
  • 换一种声明形式的话:
    • var sensor func() kelvin

一等函数 - 图1

将函数传递给其它函数

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "time"
  6. )
  7. type kelvin float64
  8. func measureTemperature(samples int, sensor func() kelvin) {
  9. for i := 0; i < samples; i++ {
  10. k := sensor()
  11. fmt.Printf("%vº K\n", k)
  12. time.Sleep(time.Second)
  13. }
  14. }
  15. func fakeSensor() kelvin {
  16. return kelvin(rand.Intn(151) + 150)
  17. }
  18. func main() {
  19. measureTemperature(3, fakeSensor)
  20. }

声明函数类型

  • 为函数声明类型有助于精简和明确调用者的代码。
    • 例如:type sensor func() kelvin
    • 所以:func measureTemperature(samples int, s func() kelvin)
    • 可以精简为:func measureTemperature(samples int, s sensor)

闭包和匿名函数

  • 匿名函数就是没有名字的函数,在 Go 里也称作函数字面值。
  • 因为函数字面值需要保留外部作用域的变量引用,所以函数字面值都是闭包的。
  1. package main
  2. import "fmt"
  3. type kelvin float64
  4. // sensor function type
  5. type sensor func() kelvin
  6. func realSensor() kelvin {
  7. return 0
  8. }
  9. func calibrate(s sensor, offset kelvin) sensor {
  10. return func() kelvin {
  11. return s() + offset
  12. }
  13. }
  14. func main() {
  15. sensor := calibrate(realSensor, 5)
  16. fmt.Println(sensor())
  17. }
  • 闭包(closure)就是由于匿名函数封闭并包围作用域中的变量而得名的。

作业题

  • 修改这段程序:
    • 声明一个变量,并将其用作 calibrate 函数的 offset 实参,而不是使用字面值数字 5。在此之后,即使修改变量,调用 sensor() 的结果也仍然为 5。这是因为 offset 形参接受的是实参的副本而不是引用,也就是所谓的按值传递。
    • 使用 calibrate 函数和今天讲的 fakeSensor 函数以创建新的 sensor 函数,然后多次调用这个新的 sensor 函数,看看它是否每次都会调用 fakeSensor 函数并产生随机的读数。

一等函数 - 图2

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. )
  6. type kelvin float64
  7. type sensor func() kelvin
  8. func fakeSensor() kelvin {
  9. return kelvin(rand.Intn(151) + 150)
  10. }
  11. func calibrate(s sensor, offset kelvin) sensor {
  12. return func() kelvin {
  13. return s() + offset
  14. }
  15. }
  16. func main() {
  17. var offset kelvin = 5
  18. sensor := calibrate(fakeSensor, offset)
  19. for count := 0; count < 10; count++ {
  20. fmt.Println(sensor())
  21. }
  22. }