fmt.Scanf in a loop

1,921 views
Skip to first unread message

Erik Adelbert

unread,
Aug 13, 2010, 6:38:00 AM8/13/10
to golang-nuts
When used in a loop, I get this behavior with fmt.Scanf under $GOARCH = am64, $GOOS = darwin and release.2010-08-11 (1 known bugs; 0 unexpected bugs)
Can you reproduce this? If yes, is this expected?

Erik

-------------------- Source Code Snippet -----------------------------
package main

import "fmt"

// nonWorkingLoopScan loops forever without reading any input but the first two
func nonWorkingLoopScan() {
var p, q int

for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; /* nothing more */ {
fmt.Println(p, q)
}
}

// workingLoopScan acquires every input and eventually terminates (newline, malformed line, EOF, ...)
func workingLoopScan() {

for {
var p, q int

if n, err := fmt.Scanf("%d %d", p, q); n != 2 || err != nil {
break
}

fmt.Println(p, q)
}

}

func main() {

workingLoopScan()
nonWorkingLoopScan()

}
-------------------- End of Source Code Snippet -----------------------------

Corey Thomasson

unread,
Aug 13, 2010, 11:37:44 AM8/13/10
to Erik Adelbert, golang-nuts
On 13 August 2010 06:38, Erik Adelbert <erik...@gmail.com> wrote:
> When used in a loop, I get this behavior with fmt.Scanf under $GOARCH = am64, $GOOS = darwin and release.2010-08-11 (1 known bugs; 0 unexpected bugs)
> Can you reproduce this? If yes, is this expected?
>
> Erik
>
> -------------------- Source Code Snippet -----------------------------
> package main
>
> import "fmt"
>
> // nonWorkingLoopScan loops forever without reading any input but the first two
> func nonWorkingLoopScan() {
>        var p, q int
>
>        for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; /* nothing more */ {
>                fmt.Println(p, q)
>        }
> }


I think the second semi-colon here is what's getting you. this loop is
equivalent to

n, err = scanf
while n == 2 && err == nil:
print...

You only ever call scanf once.

roger peppe

unread,
Aug 13, 2010, 11:42:21 AM8/13/10
to Erik Adelbert, golang-nuts
On 13 August 2010 11:38, Erik Adelbert <erik...@gmail.com> wrote:
>        for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; /* nothing more */ {
>                fmt.Println(p, q)
>        }

personally, i think it would be nice if that fragment worked as you
expected, but i think you
actually want something like this:

for {
n, err := fmt.Scanf("%d %d", &p, &q)

if n != 2 || err != nil {
break
}
fmt.Println(p, q)
}

Jessta

unread,
Aug 13, 2010, 1:54:56 PM8/13/10
to Erik Adelbert, golang-nuts
On Fri, Aug 13, 2010 at 8:38 PM, Erik Adelbert <erik...@gmail.com> wrote:
> When used in a loop, I get this behavior with fmt.Scanf under $GOARCH = am64, $GOOS = darwin and release.2010-08-11 (1 known bugs; 0 unexpected bugs)
> Can you reproduce this? If yes, is this expected?

Yes, this is completely expected.
http://golang.org/doc/go_spec.html#For_statements

Go's for loops work exactly the same as C,C++,Java, etc.

> // nonWorkingLoopScan loops forever without reading any input but the first two
> func nonWorkingLoopScan() {
>        var p, q int
>
>        for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; /* nothing more */ {
>                fmt.Println(p, q)
>        }
> }

You're missing the 'PostStmt'
ie. You probably want something like:
for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; n, err
= fmt.Scanf("%d %d", &p, &q) {
fmt.Println(p, q)
}

- jessta
--
=====================
http://jessta.id.au

Erik Adelbert

unread,
Aug 13, 2010, 1:56:42 PM8/13/10
to Erik Adelbert, golang-nuts
Sorry for the typo (omitting "&" in fmt.Scanf) , the working loop scan is:

// workingLoopScan acquires every input and eventually terminates (newline, malformed line, EOF, ...)
func workingLoopScan() {

for {
var p, q int

if n, err := fmt.Scanf("%d %d", &p, &q); n != 2 || err != nil {
break
}

fmt.Println(p, q)
}

}

But I still do not get why the following is incorrect:

// nonWorkingLoopScan loops forever without reading any input but the first two
func nonWorkingLoopScan() {
var p, q int

for n, err := fmt.Scanf("%d %d", &p, &q); n == 2 && err == nil; /* nothing more */ {
fmt.Println(p, q)
}
}

I have checked the code using production rules (language specs) and it seems perfectly legal to have an EmptyStmt (which is a SimpleStmt) as PostStmt in a ForClause (ie. the second ";" should not get in the way as suggested by Corey). Moreover, i expect the compiler to produce (almost) the same binary code for the workingLoopScan() and nonWorkingLoopScan() functions and this is obviously not the case in the current release.

By the way, I have also noticed that omitting "&" in fmt.Scanf as in "fmt.Scanf("%d %d", p, q)" doesn't produce any error during compilation nor runtime.

e.

Erik Adelbert

unread,
Aug 13, 2010, 2:03:17 PM8/13/10
to Jessta, golang-nuts
Jessta,

You are right!

In the process of rewriting "while" using "for" I was confused. It is indeed totally expected in a for loop to have an initStmt which runs once (init after all) and a postStmt to run every time!

Sorry for the last message ;)

e.

Steven

unread,
Aug 13, 2010, 4:13:55 PM8/13/10
to Erik Adelbert, Jessta, golang-nuts
Yes, this is the consequence of lobbing out the while loop by
generalizing the for loop: no per-iteration simple statement pattern
as seen in if and switch statements. It's easy enough to work around,
but it is a bit of a thought trap.
Reply all
Reply to author
Forward
0 new messages