我的迭代函数有什么问题?

huangapple go评论102阅读模式
英文:

What is wrong with my iterate function

问题

我正在尝试迭代一个简单的链表。这应该很简单,但是它不起作用。问题出在iterate函数中。

package main

import (
	"fmt"
	"time"
)

type Node struct {
	Next  *Node
	Value int
}

func main() {
	//Load up 100 *Node in a linked list (albeit a simple one)
	head := &Node{Value: 0}
	current := head
	for i := 1; i < 100; i++ {
		current.Next = &Node{Value: i}
		current = current.Next
		fmt.Printf("current %p %+v\n", current, current)
	}

	iterate(head)
}

//Iterate through the list starting at head. It never 
//advances past the first "Next", loops forever.
func iterate(head *Node) {
	for n := head.Next; n != nil; head = n {
		fmt.Printf("head %+v n %+v\n", head, n)
		time.Sleep(time.Second * 1)
	}
}

iterate函数的输出结果如下:

head &{Next:0x20818c230 Value:0} n &{Next:0x20818c280 Value:1}
head &{Next:0x20818c280 Value:1} n &{Next:0x20818c280 Value:1}
head &{Next:0x20818c280 Value:1} n &{Next:0x20818c280 Value:1}
head &{Next:0x20818c280 Value:1} n &{Next:0x20818c280 Value:1}
head &{Next:0x20818c280 Value:1} n &{Next:0x20818c280 Value:1}

出于好奇,我尝试了另一个版本的iterate循环,它使用一个函数来获取.Next。我的想法是,也许head.Next总是指向原始head,因为存在某种循环优化。但这个理论似乎是错误的。

func iterate(head *Node) {
	getNext := func (n *Node) *Node {
		return n.Next
	}

	for n := getNext(head); n != nil; head = n {
		fmt.Printf("head %+v n %+v\n", head, n)
		time.Sleep(time.Second * 1)
	}
}

天哪,我是不是看不到问题所在?我在循环体执行后将head设置为n,即下一个节点。难道下一个head.Next不应该返回后续的节点,直到我们到达一个空节点并退出循环吗?

--- 更新 ---

我对iterate进行了以下修改,现在更加简洁且正确:

func iterate(head *Node) {
	for ; head != nil; head = head.Next {
		fmt.Printf("head %+v head.Next %+v\n", head, head.Next)
	}
}
英文:

I am attempting to iterate over a simple linked list. This should be so simple, but it's not working. The iterate function contains the issue.

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

type Node struct {
	Next  *Node
	Value int
}

func main() {
	//Load up 100 *Node in a linked list (albeit a simple one)
	head := &amp;Node{Value: 0}
	current := head
	for i := 1; i &lt; 100; i++ {
		current.Next = &amp;Node{Value: i}
		current = current.Next
		fmt.Printf(&quot;current %p %+v\n&quot;, current, current)
	}

	iterate(head)
}

//Iterate through the list starting at head. It never 
//advances past the first &quot;Next&quot;, loops forever.
func iterate(head *Node) {
	for n := head.Next; n != nil; head = n {
		fmt.Printf(&quot;head %+v n %+v\n&quot;, head, n)
		time.Sleep(time.Second * 1)
	}
}

The output of iterate looks something like:

head &amp;{Next:0x20818c230 Value:0} n &amp;{Next:0x20818c280 Value:1}
head &amp;{Next:0x20818c280 Value:1} n &amp;{Next:0x20818c280 Value:1}
head &amp;{Next:0x20818c280 Value:1} n &amp;{Next:0x20818c280 Value:1}
head &amp;{Next:0x20818c280 Value:1} n &amp;{Next:0x20818c280 Value:1}
head &amp;{Next:0x20818c280 Value:1} n &amp;{Next:0x20818c280 Value:1}

For kicks I tried another version of the iterate loop that uses a function to fetch .Next. My thinking was that maybe head.Next was always pointing at my original head due to some sort of loop optimization. That theory seems incorrect.

func iterate(head *Node) {
	getNext := func (n *Node) *Node {
		return n.Next
	}

	for n := getNext(head); n != nil; head = n {
		fmt.Printf(&quot;head %+v n %+v\n&quot;, head, n)
		time.Sleep(time.Second * 1)
	}
}

Gosh, am I just not seeing it? I set head to n after the loop body executes which is equal to the next Node. Shouldn't the next head.Next return the subsequent Node until we get to a nil node and exit the loop?

--- Update ---

I've come up with the following modification to iterate which is so much cleaner and actually correct now:

func iterate(head *Node) {
	for ; head != nil; head = head.Next {
		fmt.Printf(&quot;head %+v head.Next %+v\n&quot;, head, head.Next)
	}
}

答案1

得分: 4

查看For语句规范

  • 循环的“初始化语句”部分(n := head.Next)只会被评估一次。
  • 后置语句会将head重置为n的初始值(getNext(head))。

因此导致了无限循环。

n := getNext(head)放在循环内部会更好,就像这个可工作的示例一样:

for n := head; n != nil; head = n {
    fmt.Printf("head %+v n %+v\n", head, n)
    time.Sleep(time.Second * 1)
    n = head.Next
}
英文:

Looking at the For statement spec:

  • The "init statement" part of your loop (n := head.Next) is evaluated only once.
  • The post statement keeps resetting head to the initial value of n (getNext(head)).

Hence the inifinite loop.

Putting n := getNext(head) within the loop should be better, as in this working example:

for n := head; n != nil; head = n {
	fmt.Printf(&quot;head %+v n %+v\n&quot;, head, n)
	time.Sleep(time.Second * 1)
	n = head.Next
}

答案2

得分: 3

n := head.Next 只运行一次。

for ①; ②; ③ { ④ } 中,

它运行 ① - ②④③ - ②④③ - ②④③ - ...

所以你应该将迭代放在 ③ 中,当然,你也可以将其放在 ② 或 ④ 中,但这不太明显。

英文:

n := head.Next just run once.

in for ①; ②; ③ { ④ }

it runs ① - ②④③ - ②④③ - ②④③ - ...

so you should put the iteration in ③, of course, you can put it in ② or ④ but it's not very obvious.

huangapple
  • 本文由 发表于 2015年2月27日 14:58:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/28759275.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定