【问题标题】:How to break out of nested loops in Go?如何打破 Go 中的嵌套循环?
【发布时间】:2019-01-30 10:41:40
【问题描述】:

我有一个外循环和内循环,每个循环都在一个范围内进行。我想在内循环内满足条件时退出外循环。

我有一个解决方案,它使用两个'break',一个在内部循环内部,一个在外部循环内部,就在内部循环外部(一个非常简化的演示案例):

package main

import (
    "fmt"
)

func main() {

    word := ""
    for _, i := range("ABCDE") {
        for _,j := range("ABCDE") {
            word = string(i) + string(j)
            fmt.Println(word)
            if word == "DC" {
                break
            }
        }
        if word == "DC" {
            break
        }
    }
    // More logic here that needs to be executed
}

Go Playground

这个解决方案没有问题,但在我看来它只是打补丁和丑陋。有没有更好的方法来做到这一点?

我可以尝试在上一个解决方案的外循环之外再设置一个 for 条件循环,并使用标签并使用 continue 与标签。但正如您所见,这种方法并不比使用 break 的解决方案更优雅。

package main

import (
    "fmt"
)

func main() {

    word := ""

Exit:
    for word != "DC" {
        for _, i := range "ABCDE" {
            for _, j := range "ABCDE" {
                word = string(i) + string(j)
                fmt.Println(word)
                if word == "DC" {
                    continue Exit
                }
            }
        }
    }
    // More logic here that needs to be executed
}

Go Playground

我在这里看到了与其他语言(C、C#、Python 等)有关的类似问题。但我真正感兴趣的是 Go 结构是否有任何技巧,例如“for select”。

【问题讨论】:

  • 对于这种特殊情况,只需要return 是否合适?
  • 它适用于我过于简化的示例。但是,对于该功能来说,还有进一步的逻辑。抱歉没有更清楚。我将修改问题以避免混淆。
  • 如果你用函数代替呢?返回 truefalse 甚至是单词本身。您可以在该函数中使用return
  • 你考虑过goto吗?
  • 您的选项是 flag var、label 或 return。差不多就是这样。

标签: loops go


【解决方案1】:

使用break {label} 可以根据需要跳出任何嵌套的循环。只需将标签 放在您想要跳出的 for 循环之前。这与执行goto {label} 的代码非常相似,但我认为更优雅一点,但我猜是见仁见智。

package main

func main() {
    out:
    for i := 0; i < 10; i++ {
        for j := 0; j < 10; j++ {
            if i + j == 20 {
                break out
            }
        }
    }
}

更多详情:https://www.ardanlabs.com/blog/2013/11/label-breaks-in-go.html

【讨论】:

  • 这显然是正确的答案。 “返回”解决方案或多或少是一种解决方法(如果在函数内部可能有用,但并非总是如此)
【解决方案2】:

使用功能

package main

import (
    "fmt"
)

func getWord() string {
    word := ""
    for word != "DC" {
        for _, i := range "ABCDE" {
            for _, j := range "ABCDE" {
                word = string(i) + string(j)
                fmt.Println(word)
                if word == "DC" {
                    return word
                }
            }
        }
    }
    return word
}

func main(){
    word := getWord()
}

编辑:感谢@peterSO,他指出了细节中的一些错误并提供了这个游乐场https://play.golang.org/p/udcJptBW9pQ

【讨论】:

  • 肯定是一个不错的选择。但是代码块不是通用的/在其他任何地方都用于将其限定为函数。会赞成这个想法。
  • 您没有将它用作函数,因为它可以在其他地方使用,或者是通用的。您正在使用一个函数,因为它可以解决您的问题。 +1
  • @peterSO 你是对的!我编辑了我的答案,并在答案中包含了您的游乐场链接。谢谢!
  • 在仔细权衡选项后,我在我的代码中采用了这个。非常感谢您的建议。
【解决方案3】:

转到呢?

package main

import (
    "fmt"
)

func main() {

    word := ""

        for _, i := range "ABCDE" {
            for _, j := range "ABCDE" {
                word = string(i) + string(j)
                fmt.Println(word)
                if word == "DC" {
                    goto Exit
                }
            }
        }
    Exit: // More logic here that needs to be executed
}

【讨论】:

  • 它会起作用,但就像我说的,我想尽可能避免使用标签。
  • goto 是邪恶的,但在这种情况下,我认为它是最简单的解决方案。
【解决方案4】:

最直接的似乎是这样的:

func main() {
    word := ""
    isDone := false
    for _, i := range("ABCDE") {
        for _,j := range("ABCDE") {
            word = string(i) + string(j)
            fmt.Println(word)
            isDone = word == "DC"
            if isDone {
                break
            }
        }
        if isDone {
            break
        }
    }
    //  other stuff
}

使用生成器的替代方案

但是,您也可以使用生成器来创建单词序列,如下所示:

func makegen () chan string {
    c:= make(chan string)
    go func () {
        for _, i := range ("ABCDE") {
            for _, j := range ("ABCDE") {
                c <- string(i) + string(j)
            }
        }
        close (c)
    }()

    return c
}


func main() {
    word := ""
    for word = range makegen() {
        fmt.Println (word)
        if word == "DC" {
          break
        }
    }
    // other code
}

【讨论】:

  • 这不是和我的第一个解决方案一样吗?
  • @Jayachandran 是的,更改为中断条件被评估一次,这样您就没有重复检查。我要说的是,您作为第一个解决方案所拥有的似乎很好,尽管如果可能的话,我会进行更改以评估退出条件一次,然后只检查评估的条件。这样一来,您就可以降低未来无法在所有正确位置进行更改的风险。
  • 哦,是的,这是有道理的。到目前为止,我喜欢 makegen 解决方案。感谢那。在我选择接受之前将等待其他答案。
【解决方案5】:

只需推迟您需要做的任何事情并正常返回。

package main

import (
    "fmt"
)

func main() {
    defer func() {
        // More logic here that needs to be executed
    }()

    word := ""

    for _, i := range "ABCDE" {
        for _, j := range "ABCDE" {
            word = string(i) + string(j)
            fmt.Println(word)
            if word == "DC" {
                return
            }
        }
    }
}

【讨论】:

    【解决方案6】:

    将你的 for 循环包装在一个匿名的自调用函数中,然后在你想中断时返回

    package main
    
    func main() {
        func() {
            for i:= 0; i < 100; i++ {
                for j:= 0; j < 100; j++ {
                    if (i == 5 && j == 5) {
                        return
                    }
                }
            }
        }()
    }
    

    【讨论】:

      猜你喜欢
      • 2012-03-30
      • 2012-01-19
      • 2022-01-14
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      相关资源
      最近更新 更多