前言
比如這樣的需求, 遍歷一個 切片, 切片內容是切片1, 需求是判斷切片1中某個是否有相應數據, 有就返回
正文
我們需要考慮的是在寫兩層遍歷時如何在獲取結果后結束這兩層遍歷
變量法
設置一個變量, 在外層監聽該變量, 獲取到結果后修改該變量
func main() {
t := [][]int{{1, 2, 3, 4, 5}, {5, 6, 7, 8, 9}}
s := false
for _, v := range t {
for _, v1 := range v {
if v1 == 5 {
s = true // 判斷成立修改標量退出二層循環
break
}
}
if s { // 檢測標量結果,為true代表命中
break
}
}
}
缺點很明顯, 如果套更多層需要在每個層都寫一個判斷標量的邏輯一層層退出
goto
func main() {
t := [][]int{{1, 2, 3, 4, 5}, {5, 6, 7, 8, 9}}
for _, v := range t {
for _, v1 := range v {
if v1 == 5 {
goto Loop // 跳轉到注冊名為Loop的標記處,中間的代碼直接跳過
}
}
}
fmt.Println("nil")
return // 此處return防止繼續執行loop的代碼
Loop: // Loop標記處, Loop只是一個標記,正常順序執行也會執行Loop的代碼,,一般叫Loop,可以自定義,與上面一致即可
fmt.Println("success")
}
此方法不止適用於循環,實際上他可以貼在任意地方,比如
func main() {
fmt.Println(1)
goto Loop
fmt.Println(2)
Loop:
fmt.Println(3)
}
上面的 2 永遠不會打印
這種方法的問題是會破壞正常的代碼結構, 一個項目的代碼必定是很多的, 有很多邏輯, 使用goto會破壞原有的代碼結構, 大大降低了可讀性和可維護性, 因此請盡可能的避免使用 goto
break Label
給某一層循環設置一個Label,指定跳過某一個Label
func main() {
t := [][]int{{1, 2, 3, 4, 5}, {5, 6, 7, 8, 9}}
s := false
Loop: // 標記該循環為Loop,一般叫Loop,可以自定義,與下面一致即可
for _, v := range t {
for _, v1 := range v {
if v1 == 5 {
s = true
break Loop // 跳出Loop標記的循環
}
}
}
if s {
fmt.Println("success")
}
}
相比於 goto, break Label 只能在循環中使用, Loop只能注冊到循環中, goto是跳轉到某行執行, break是跳出Loop標記的循環,相對來說限制大一些, 沒有那么隨意, 而相比於 方法一,則無需寫多層判斷, 需要注意的是, break Label 的 Label 不一定是頂層, 可以在任意一層
func main() {
t := [][][]int{{{1, 2, 3, 4, 5}}, {{5, 6, 7, 8, 9}}}
s := false
for _, v := range t {
Loop: // 標記該循環為Loop
for _, v1 := range v {
for _, v2 := range v1 {
if v2 == 5 {
s = true
break Loop // 跳出Loop標記的循環
}
}
}
}
if s {
fmt.Println("success")
}
}
單獨寫一個函數
這個不多說, 因為 return 直接退出函數了
func t1(t [][][]int) bool {
for _, v := range t {
for _, v1 := range v {
for _, v2 := range v1 {
if v2 == 5 {
return true
}
}
}
}
return false
}
func main() {
t := [][][]int{{{1, 2, 3, 4, 5}}, {{5, 6, 7, 8, 9}}}
fmt.Println(t1(t))
}
總結
條條大路通羅馬, 其實哪種都可以, 但是我還是推薦使用函數的形式, 因為goto和lable或多或少都會降低可讀性. 何況, 如果真的出現多個循環在一個函數, 應該思考是不是設計的有問題或者實現的有問題.