Go闭包捕获规则与延迟捕获规则有何不同?
下面的Go代码演示了延迟闭包和Go闭包之间的闭包捕获规则的区别。在一个教程中,我被告知for循环变量的作用域仅限于循环体,但这里似乎有所不同Go闭包捕获规则与延迟捕获规则有何不同?,go,Go,下面的Go代码演示了延迟闭包和Go闭包之间的闭包捕获规则的区别。在一个教程中,我被告知for循环变量的作用域仅限于循环体,但这里似乎有所不同 package main import "fmt" func deferred() { for i := 0; i < 5; i++ { defer fmt.Println(i) } } func cps() { clo := new(func()) *clo = func() {} de
package main
import "fmt"
func deferred() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
func cps() {
clo := new(func())
*clo = func() {}
defer func() { (*clo)() }()
for i := 0; i < 5; i++ {
oldClo := *clo
*clo = func() {
fmt.Println(i)
oldClo()
}
}
}
func cpsCpy() {
clo := new(func())
*clo = func() {}
defer func() { (*clo)() }()
for i := 0; i < 5; i++ {
oldClo := *clo
cpy := i
*clo = func() {
fmt.Println(cpy)
oldClo()
}
}
}
func main() {
fmt.Println("defer")
deferred()
fmt.Println("cps")
cps()
fmt.Println("cpsCpy")
cpsCpy()
}
如果差异是有意造成的,那么有哪些不同的用例可以证明这一点?这一点很清楚。对于任何关心变量捕获规则之类的东西的人来说,该规范通常都是至关重要的读物,相对而言,它的内容相对较短。这里说的是:
每次执行“defer”语句时,调用的函数值和参数都会像往常一样求值并重新保存,但不会调用实际函数
它也包括在
总是很难让每个人都满意地证明设计决策的合理性,尤其是延迟
的设计决策有时会让人们感到惊讶,特别是当您执行延迟f1(f2(x))
并忘记f2(x)
时,将立即进行评估
不过,有一件事可能有助于记住它,那就是defer
(或go
)之后的是函数调用,而只是函数调用本身(甚至不是参数的计算)会随着时间的推移而改变。闭包定义了一个代码块,在执行代码时访问变量
由于您对闭包中获取的变量捕获类型的用途感到好奇,因此,如果您将闭包与类似于
ForEach
的方法一起使用,这是一个很有用的示例:
myBTree.ForEach(func (key, value []byte) {
//...
})
如果像在常规的for
循环中一样,大括号之间的代码可以修改外部范围中的变量,那就很好了。这要求闭包访问变量,而不仅仅是特定时间的变量值。这一点很清楚。对于任何关心变量捕获规则之类的东西的人来说,该规范通常都是至关重要的读物,相对而言,它的内容相对较短。这里说的是:
每次执行“defer”语句时,调用的函数值和参数都会像往常一样求值并重新保存,但不会调用实际函数
它也包括在
总是很难让每个人都满意地证明设计决策的合理性,尤其是延迟
的设计决策有时会让人们感到惊讶,特别是当您执行延迟f1(f2(x))
并忘记f2(x)
时,将立即进行评估
不过,有一件事可能有助于记住它,那就是defer
(或go
)之后的是函数调用,而只是函数调用本身(甚至不是参数的计算)会随着时间的推移而改变。闭包定义了一个代码块,在执行代码时访问变量
由于您对闭包中获取的变量捕获类型的用途感到好奇,因此,如果您将闭包与类似于
ForEach
的方法一起使用,这是一个很有用的示例:
myBTree.ForEach(func (key, value []byte) {
//...
})
如果像在常规的
for
循环中一样,大括号之间的代码可以修改外部范围中的变量,那就很好了。这要求闭包访问变量,而不仅仅是特定时间的变量值。没有区别,您可以看到:@JimB好吧,FAQ让它看起来像一个for循环创建了两个作用域,一个用于迭代器,一个用于主体内部。但是,延迟似乎还是不同的:它并没有创建一个闭包(正如我认为有些地方所宣称的那样)。它首先对非引用变量执行一个奇怪的基于值的捕获,然后进行闭包,不,不,范围在所有情况下都是相同的。在cps
中,您只需捕获单个i
变量并将其打印5次。@JimB恐怕我还是有点困惑。您是否可以写一个答案,或者将for扩展为一个go“while”循环,以演示作用域到底是什么?为什么不延迟捕获单个i变量呢?是的,@JimB正确地认为闭包的行为是指定的。延迟所做的根本不是变量捕获;它被指定评估函数参数并保存它们。没有区别,您可以看到:@JimB好吧,FAQ让它看起来像是一个for循环创建了两个作用域,一个用于迭代器,一个用于主体内部。但是,延迟似乎还是不同的:它并没有创建一个闭包(正如我认为有些地方所宣称的那样)。它首先对非引用变量执行一个奇怪的基于值的捕获,然后进行闭包,不,不,范围在所有情况下都是相同的。在cps
中,您只需捕获单个i
变量并将其打印5次。@JimB恐怕我还是有点困惑。您是否可以写一个答案,或者将for扩展为一个go“while”循环,以演示作用域到底是什么?为什么不延迟捕获单个i变量呢?是的,@JimB正确地认为闭包的行为是指定的。延迟所做的根本不是变量捕获;它被指定对函数args求值并保存它们。好的,所以defer不会创建闭包,它是一个完整的独立语句,它有自己的行为,需要单独关注。在我看来,这里不合理的决定是闭包捕获引用(而不是使用参数,如值捕获,引用类型除外);这看起来就像是一种容易出错的方法,它可以帮助那些想让这种行为发生的人避免写额外的一行创建指针。因此,感谢您回答我问题的第一部分。请你回答第二个问题好吗?我不需要被说服,但就我自己的知识而言,我希望看到引用捕获对我有什么帮助。@VF1:defer并不是很特别,只是函数参数总是在调用点求值。如果你推迟闭包,效果也是一样的:@JimB好吧,你是说它不特别,但是