如下是我的测试代码(一个素数筛),注释掉的方式会跑超时。
// go test -timeout 30s -run ^TestChannelPrime$ package-path -v
func TestChannelPrime(t *testing.T) {
for i := range primeGenerator() {
if i > 13 {
break
}
t.Log(i)
}
}
func primeGenerator() <-chan int {
res := make(chan int)
// 2 ->
// 3 3 ->
// 4
// 5 5 5 ->
// 6
// 7 7 7
// 9 9
go func() {
for curChannel := func() <-chan int {
naturalNumber := make(chan int)
go func() {
for i := 2; ; i++ {
naturalNumber <- i
}
}()
return naturalNumber
}(); ; {
curPrimeNumber := <-curChannel
res <- curPrimeNumber
curChannel = func(curChan <-chan int, curPrime int) <-chan int {
nextChan := make(chan int)
go func() {
for {
if i := <-curChan; i%curPrime != 0 {
nextChan <- i
}
}
// for i := <-curChan; i%curPrime != 0; {
// nextChan <- i
// }
}()
return nextChan
}(curChannel, curPrimeNumber)
}
}()
return res
}
问题:Golang,为何for-loop的条件判断与for控制块里做if判断存在区别
答:其实跟golang没关系,这两个for代码块的区别是,注释掉的代码中的条件判断为否就跳出循环了,而预期是死循环,只是是否取值是根据条件判断的。
补充问题:闭包和传参存在差异(测试代码中curChan和curPrime的位置)
答:golang的匿名函数,即函数字面量就是闭包,不同于Java,golang中被闭包的对象是共享的,也是可修改的(见问题评论)。这里的代码,curChan必须从curChannel拷贝值已形成自己的输出通道,否则,如果只用闭包的curChannel,那么,所有函数字面量协程的输出通道都将指向(其实是值赋值、完全被新值覆盖)同一个新的curChannel。而这里的curPrimeNumber其实是可以用闭包的,因为在外层循环的每一次,curPrimeNumber都是一个全新赋值的局部对象,这也许就是用:=
将初始化凸显出来的缘由。更新后的代码见:https://www.cnblogs.com/qqiwei/p/16513559.html
函数单独定义出来,代码能清晰很多。
闭包如果引用了外部的变量,那么外部变量变了,闭包中引用的变量也会变。
而传参的话,外部变量传入函数之后,外部变量变了,函数内的变量不会变。
https://stackoverflow.com/questions/68093946/why-variable-used-in-lambda-expression-should-be-final-or-effectively-final
– qqiwei 2年前https://go.dev/ref/spec#Function_literals
– qqiwei 2年前