This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Go 语言 for 和 range 的实现 · /golang-for-range #141
Comments
讲得真深奥,看一次好多点都没看懂 |
因为上下文的关联其实比较多,有什么疑惑可以问一下 |
讲得很好,也很深奥,收藏一波以后慢慢回顾 |
这是得有很好的C基础才能看的懂呀。犟得很好,但是我看不懂😂 |
这其实都是 Go 语言啊 |
写的真好,学习了,大佬,可以弄个RSS吗,方便大家订阅,公众号已关注 |
|
您好,冒昧打扰,在看 func Test(t *testing.T) {
var m = map[string]int {
"A": 21,
"B": 22,
"C": 23,
}
counter := 0
for k, v := range m {
if counter == 0 {
delete(m, "A")
}
counter++
}
fmt.Println(counter)
} 这样一段代码输出的结果可能有两种: 您在讲解使用
对于 map,根据您给出的编译转换后的循环,也执行了 |
我好像明白了: |
可以的,给你点赞,我刚想给你回复 |
哈哈哈感谢大佬的教程,写的真的棒,受益匪浅! |
你好,写得真不错,想问一下,这里的range实现,看下来就是在原来的函数里面插入一些控制变量,转换成普通的for函数吧,比较奇怪的是增加的变量是怎么命名的,会不会和用户自己定义的变量冲突了。 |
编译器会通过 temp 函数生成一个唯一的标识符,你可以看看 |
@draveness 谢谢这么快的回复,看了代码,确实是用这个函数来生成临时变量的。
|
这段话 我读了5 6遍 才读出 其中意思 如果加2个字,是不是更好呢? 这个变量 v2 会在每一次迭代被重新赋值而覆盖 经常来看看博主的文章,受益良多! |
我来加一下 |
ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
tmp := ha[hv1]
v1, v2 := hv1, tmp.
...
} 而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。 这里 v2是不是应该在for循环外定义? |
说下你的理由? |
而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。 |
引用你上一个神奇的指针的例子,如果v2在循环内定义赋值的话,每次apend的&v2 都是不同的。并不会出现333。 2020-10-19 UPDATES: 已修复变量初始化位置 |
确实是这样的,我回头改一下 |
Left 不是「中止条件」,是继续条件 2020-05-06 UPDATES: 已修复 |
不瞒您说,我确实也在看神奇的指针这个案例的时候看蒙圈了,直到看完解释也没理解。同一句话读了5,6次也没想明白,最后我自己把地址打出来,一切谜团才能解开。。。原谅我的迟钝(手动滑稽) func main() {
arr := []int{1, 2, 3}
newArr := []*int{}
for _, v := range arr {
fmt.Println("")
fmt.Printf("origin addr: %p value: %v", &v, v)
newArr = append(newArr, &v)
}
for _, s := range newArr {
fmt.Println("")
fmt.Printf("addr: %p value: %v", s, *s)
}
} 输出结果: origin addr: 0xc0000a67a8 value: 1
origin addr: 0xc0000a67a8 value: 2
origin addr: 0xc0000a67a8 value: 3
addr: 0xc0000a67a8 value: 3
addr: 0xc0000a67a8 value: 3
addr: 0xc0000a67a8 value: 3 |
什么情况下会是2啊?为啥我跑了几次都是3? |
你跑的是哪段代码? |
就前面这哥们说的counter is 3的代码,大佬你一追问,我又找来重新跑了一下,一下就出现2的情况,没问题了,打扰。 |
为什么“v2在循环内定义赋值的话,每次apend的&v2 都是不同的”,这里即使每次从栈上分配v2,&v2地址也会是相同的吧 |
不知道python有没有类似的文档,讲的很好,但是真的很难看懂 |
这里其实也解释了"循环永动机"现象,建议行文可以显示地点一下。 2020-10-26 UPDATES: 已添加 |
ha := s
for hv1 := 0; hv1 < len(ha); {
hv1t := hv1
hv2 := rune(ha[hv1])
if hv2 < utf8.RuneSelf {
hv1++
} else {
hv2, hv1 = decoderune(h1, hv1)
}
v1, v2 = hv1t, hv2
} 关于字符串这段的描述,decoderune的第一个参数应该是ha吧? 2020-10-26 UPDATES: 已修复 index 551f269..683d6fd 100644
--- a/design-and-implementation-of-go/content/docs/part2-foundation/ch05-keyword/golang-for-range.md
+++ b/design-and-implementation-of-go/content/docs/part2-foundation/ch05-keyword/golang-for-range.md
@@ -499,7 +499,7 @@ for hv1 := 0; hv1 < len(ha); {
if hv2 < utf8.RuneSelf {
hv1++
} else {
- hv2, hv1 = decoderune(h1, hv1)
+ hv2, hv1 = decoderune(ha, hv1)
}
v1, v2 = hv1t, hv2
} |
为什么觉得是 ha 呢,ha 只是原有字符串的拷贝 |
首先在给定的这段代码中h1是个未定义的变量,表述上有问题 |
这段没看懂
重新看了下,确实是这样的,已经改了 |
我觉得循环永动机那个位置应该不是因为遍历的拷贝所以才会不出现循环永动机,毕竟切片是引用对象,而是因为他直接对拷贝后的数组对象求了len,然后循环终止的条件是小于一开始求的len,像如下代码的话那么就会出现索引越界panic,因为他的终止条件是<len=3 arr := []int{1, 2, 3}
for i := range arr {
fmt.Println(arr[i])
arr = arr[:2]
} 2020-11-06 UPDATES: 已修复 |
你说的有道理,后面会改一下 |
博主你好,最近重新看的时候用之前评论的代码改动了一下 func main() {
var m = map[string]int{
"A": 21,
"B": 22,
"C": 23,
}
counter := 0
for k, v := range m {
if counter == 0 {
m["D"] = 20
}
counter++
fmt.Println(k, v)
}
fmt.Println(counter)
} 最终结果有时是4可以遍历到D有时候是3没有遍历到D,这是为什么呢,感觉哈希表的遍历过程有点不好理解,是不是有可能是一开始的startBucket是空桶然后将D插入到了这个空桶所以就没有遍历到 |
我在本地和 Go Playground 测试了几次都可以遍历到 D https://play.golang.org/p/fSPpo4_-k57 |
我测试的时候也是大部分时候可以遍历到D,但仍然有时候遍历不到,我刚刚用playground也试过了有遍历不到D的情况,就我猜是不是有可能一开始选择的遍历桶startBucket可能是个空桶,然后遍历过去之后将D插入到了已经遍历过的桶中,所以就有可能会出现遍历不到D的情况 |
同时不光是切片的range遍历,哈希表、通道和字符串的遍历的元素应该都是在外层循环定义,形如 for i, v := range map/string {} 给我们使用的那个v如果打印地址的话应该都是不变的,如果在循环内部将这个v用 := 赋值给了另一个变量那么就不会出现打印的地址都是同一个了 |
不看十遍对不起大佬这么细致的讲解:D 太精彩了 |
突然感觉 for range 对Map的遍历,意义不大。。。 |
不太明白你在这里提出的问题 |
就是感觉 for range 有好多坑,不如用 for 循环 |
最后的通道部分,“如果存在当前值,会为 v1 赋值并清除 hv1 变量中的数据,然后重新陷入阻塞等待新数据;”,这里特别强调清除hv1变量中的数据有什么特别的意义吗? 又看了一下walkrange的实现还真有注释说明这个清除hv1的意义(issue15281),是为了blocking在chan上的时候可以回收掉上一个获取的变量。只在chan里传递的变量类型占空间特别大时才有意义。 |
我试了下确实也会出现遍历不到D,我觉得你说的这个原因是有可能的,希望博主能求证下 |
但是不用range没法遍历map吧?用反射? |
所以map的本质不是像slice那样的SliceHeader而是hmap的指针? |
最近跟朋友讨论了下 for k, v := range a 的实现,a是slice or array的时候,要复制这个行为是不是lang spec定义的行为?
|
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
https://draveness.me/golang-for-range
The text was updated successfully, but these errors were encountered: