Go语言为什么采用晚绑定?

作者站长头像
站长
· 阅读数 4

创建一个空数组,数组元素类型为 void -> int 的函数,最后遍历数组并依次调用数组内的函数

~/go % cat arr_of_fns.go                 0:31 package main

import "fmt"

const LS_SIZE int = 5

func main() {
        // Creating empty array
        var fns [LS_SIZE]func() int
        // Loop
        for i := 0; i < LS_SIZE; i++ {
                fns[i] = func() int {
                        return i
                }
        }
        for n := 0; n < LS_SIZE; n++ {
                fmt.Printf("fns[%d]: %d\n", n, fns[n]())                                            }                                     }
~/go % go run arr_of_fns.go              0:31
fns[0]: 5
fns[1]: 5
fns[2]: 5
fns[3]: 5
fns[4]: 5
  • 循环变量 i 在离开循环块后就无法访问了,这一点和许多编译型语言一致
  • 但每个匿名函数返回的值一样,是循环变量最终的值,这一点又和 Python,Ruby,Groovy 等语言一样

为什么 Go 采用晚绑定?Ruby,Python 可以通过给函数添加默认参数,将每次循环的值保存下来。Go 要如何解决呢?

回复
1个回答
avatar
test
2024-07-08

除了一楼的方式外,你还可以:

package main

import "fmt"

const LS_SIZE int = 5

func main() {
    // Creating empty array
    var fns [LS_SIZE]func(x int) int    // 更改此处数组函数声明
    // Loop
    for i := 0; i < LS_SIZE; i++ {
        fns[i] = func(i int) int {        // 有入参
            return i
        }
    }
    for n := 0; n < LS_SIZE; n++ {
        fmt.Printf("fns[%d]: %d\n", n, fns[n](n))    // 带入入参
    }
}

输出:

fns[0]: 0
fns[1]: 1
fns[2]: 2
fns[3]: 3
fns[4]: 4

这种闭包问题很常见,fns的值在若干次循环中都是同一个内存地址,而i也在若干次循环后,更新到了 i++ 后的值。所以在你调用 fmt.Printf("fns[%d]: %d\n", n, fns[n]()) 的时候,不论是 fns[0]() 还是 fns[4]()等等,代表的都是同一个函数对象,fns[n]() 中i得值已经在上一个for循环执行完毕被更新到了5,而故而你最终得到的结果都是5.

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容