gitalk
gitalk copied to clipboard
遍历函数?Go 1.22中隐藏的功能
遍历函数?Go 1.22中隐藏的功能
Go 1.22中可以 range 一个整数,比如下面的代码: 123for i := range 10 { fmt.Println(i)}
这个大家都已经知道了,其实对应的提案中还有一个隐藏的功能,就是可以 range 一个函数,比如下面的代码(摘自官方代码库internal/trace/v2/event.go): 12345678910111213141516171819// Frame
这个写法有点难理解
我的理解:
-
fn 姑且称为 “迭代函数”,它的参数 yield 姑且称为 “执行函数”,yield 这个名称随意,这里只是为了和 JS 中的迭代器一致。
-
传递给执行函数的参数,就是最终 range 循环的参数,“执行函数” 每执行一次,range 迭代就会执行一次。
-
迭代函数控制整个迭代周期,它结束,表示迭代结束,但是也有一种可能会提前 return ,那就是在 range 中使用了 break 或 return
-
如果 range 中断(break/return),那么迭代函数中的执行函数就会返回 false
-
此时,执行函数就不能再次执行了,否则 panic ,这就是为什么要在迭代函数中判断,如果执行函数返回 false 则 return ,整个迭代过程结束
panic: runtime error: range function continued iteration after function for loop body returned false // range 函数在 for 循环体返回 false 后继续迭代
示例:
package main
import (
"fmt"
"time"
)
func main() {
var fn = func(yield func(k, v any) bool) {
count := 1
for {
time.Sleep(time.Millisecond * 500)
r := yield(count, time.Now().Format(time.DateTime))
fmt.Printf("%d continue=%v\n", count, r)
if !r {
break
}
count++
}
fmt.Println("迭代结束")
}
for k, v := range fn {
fmt.Printf("{%v: %v}\n", k, v)
if k == 2 {
continue
}
if k == 3 {
fmt.Println("break/return")
return
}
}
}
输出:
{1: 2024-08-14 11:01:51}
1 continue=true
{2: 2024-08-14 11:01:52}
2 continue=true
{3: 2024-08-14 11:01:52}
break/return
3 continue=false
迭代结束