在Go中使用重复的函数调用进行循环

I'm looping over state changes where the change & condition are defined in a function and was hoping to write a for statement where I only declare the function call once.

This code works, but repeating message, flag = a.foo() feels messy. Is there a more effective way to write a for loop when the initial statement is the same as the post statement?

func (a *Example) foo() (string, bool) {
    // Function affects state of a
    // returns message on each step and flag if condition reached
}

func main() {
    // Step through each state change until flag is triggered
    for message, flag := a.foo(); flag; message, flag = a.foo() {
        fmt.Printf("%s
", message)
    }
}

I was hoping that there would be some kind of equivalent to how nicely it fits in to an if statement like this:

if message, flag := a.foo(); !flag {
    fmt.Printf("%s
", message)
}

In fact, I was rather hoping I could just change that if to a for but, alas, expected ';'. found '{'

I may well be asking too much, but if there is an answer I'd love to hear it. Thanks

As far as I know there is no way to do exactly what you want (though I'd love if it was possible too). You could use an "infinite" loop and write the break condition manually, although that also isn't too great, something like:

func main() {
    for {
        msg, flag := a.foo()
        if flag {
            break
        }
        fmt.Printf(...)
    }
}

If you don't ever break out of the loop early, one option would be to structure your code to use a channel and goroutine. So rather than having Example.foo return (string, bool), do something like this:

func (a *Example) foo() <-chan string {
    ch := make(chan string)
    go func() {
        // Send strings down the channel
        ch <- "first"
        ch <- "second"
        ...
        close(ch)
    }()
    return ch
}

You can then loop over the values with the following:

for message := range a.foo() {
    fmt.Printf("%s
", message)
}

As mentioned above, this pattern is not appropriate if you plan to exit the loop early. If you do, then the goroutine feeding the channel will remain blocked, and stick around in memory until the program exits.

If you don't care about the scope of flag and message you could declare them before the for loop (i.e. if you aren't trying to make flag and message local temporary variables via the initialization section of the for loop).

For example:

    var message string
    var flag bool = true

    // Step through each state change until flag is triggered
    for ; flag; message, flag = a.foo() {
        fmt.Printf("%s
", message)
    }