Skip to content

函数

函数值

函数可作为值被传递

go
func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

函数闭包

  • go函数可以是一个闭包。函数闭包可以作为变量绑定给变量,从语法上看类似于一种类型。每个被绑定的变量都拥有一个独立的函数闭包实例。
  • 闭包是一个函数值,它引用了其函数体之外的变量。闭包并不会对捕获的变量进行拷贝后才使用,而是直接修改该变量本身。
go
package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}
go
package main

import "fmt"

func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
     return i  
   }
}

func main(){
   /* nextNumber 为一个函数,函数 i 为 0 */
   nextNumber := getSequence()  

   /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   
   /* 创建新的函数 nextNumber1,并查看结果 */
   nextNumber1 := getSequence()  
   fmt.Println(nextNumber1())
   fmt.Println(nextNumber1())
}

闭包+goroutine

goroutine是go的轻量级线程,由 Golang 运行时进行管理。

有博主猜测终末地开服事件的根源是程序员踩进了goroutine+闭包的坑(当然最重要的还是他们没咋测试)。

go
package main

import (
	"fmt"
	"time"
)

func main() {
	users := []string{"A", "B", "C"}

	// =========================
	// ❌ 错误写法(变量被闭包捕获)
	// =========================
	/*
	for _, v := range users {
		go func() {
			// 闭包捕获的是变量 v 本身
			// goroutine 可能在循环结束后才执行
			// 此时 v 已经变成最后一个值
			fmt.Println("wrong:", v)
		}()
	}
	*/

	// =========================
	// ✅ 正确写法(参数传值)
	// =========================
	for _, v := range users {
		go func(u string) {	// 通过传参来立即拿到正确变量的副本,该副本是独立存储的,不会被原变量影响
			// u 是当轮 v 的副本
			// 每个 goroutine 拿到独立值
			// goroutine 内的代码会延迟执行(本轮for循环大概率会在这里面的代码运行前结束)
			fmt.Println("right:", u)
		}(v)
	}

	time.Sleep(time.Second)
}