运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • Go

    • 前言

    • Go基础知识

    • Go基本语法

    • 实战项目:简单web服务

    • 基本数据类型

    • 内置运算符

    • 分支和循环

      • if(分支结构)
      • switch(分支结构)
      • for(循环结构)
      • break和continue语句
        • 4. break和continue 语句
          • 4.1 continue 语句
          • 4.1.1 支持 label
          • 4.2 break 语句
          • 4.2.1 支持label
    • 函数 function

    • 结构体 struct

    • 方法 method

    • 实战项目:跟踪函数调用链

    • 接口 interface

    • 并发 concurrency

    • 指针

    • 实战项目:实现轻量级线程池

    • 实战项目:实现TCP服务器

    • go常用包

    • Gin框架

    • go随记

  • Python

  • Shell

  • Java

  • Vue

  • 前端

  • 编程浅尝
  • Go
  • 分支和循环
lyndon
2022-06-07
目录

break和continue语句

# 4. break和continue 语句

  • break:跳出全部循环
  • continue:跳出本次循环

# 4.1 continue 语句

如果循环体中的代码执行到一半,要中断当前迭代,忽略此迭代循环体中的后续代码,并回到 for 循环条件判断,尝试开启下一次迭代,这个时候可以使用 continue 语句来应对。

var sum int
var sl = []int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(sl); i++ {
    if sl[i]%2 == 0 {
        // 忽略切片中值为偶数的元素
        continue
    }
    sum += sl[i]
}
println(sum) // 9
1
2
3
4
5
6
7
8
9
10

这段代码会循环遍历切片中的元素,把值为奇数的元素相加,然后存储在变量 sum 中。我们可以看到,在这个代码的循环体中,如果我们判断切片元素值为偶数,就使用 continue 语句中断当前循环体的执行,那么循环体下面的sum += sl[i]在这轮迭代中就会被忽略。代码执行流会直接来到循环后置语句i++,之后对循环条件表达式(i < len(sl))进行求值,如果为 true,将再次进入循环体,开启新一次迭代。

package main

import "fmt"

func main() {
   for i := 0; i < 10; i++ {
      if i%2 == 0 {
         continue
      }
      fmt.Println(i)	// 1 3 5 7 9
   }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 4.1.1 支持 label

label 语句的作用,是标记跳转的目标。

// 使用label 改造上面的例子
func main() {
    var sum int
    var sl = []int{1, 2, 3, 4, 5, 6}

loop:
    for i := 0; i < len(sl); i++ {
        if sl[i]%2 == 0 {
            // 忽略切片中值为偶数的元素
            continue loop
        }
        sum += sl[i]
    }
    println(sum) // 9
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在这段代码中,定义了一个 label:loop,它标记的跳转目标恰恰就是我for 循环。也就是说,在循环体中可以使用 continue+ loop label 的方式来实现循环体中断,这与前面的例子在语义上是等价的。不过这里仅仅是一个演示,通常在这样非嵌套循环的场景中会直接使用不带 label 的 continue 语句。

带 label 的 continue 语句,通常出现于嵌套循环语句中,被用于跳转到外层循环并继续执行外层循环语句的下一个迭代。

func main() {
    var sl = [][]int{
        {1, 34, 26, 35, 78},
        {3, 45, 13, 24, 99},
        {101, 13, 38, 7, 127},
        {54, 27, 40, 83, 81},
    }

outerloop:
    for i := 0; i < len(sl); i++ {
        for j := 0; j < len(sl[i]); j++ {
            if sl[i][j] == 13 {
                fmt.Printf("found 13 at [%d, %d]\n", i, j)
                continue outerloop
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在这段代码中,变量 sl 是一个元素类型为[]int 的切片(二维切片),其每个元素切片中至多包含一个整型数 13。main 函数的逻辑就是在 sl 的每个元素切片中找到 13 这个数字,并输出它的具体位置信息。

那这要怎么查找呢?一种好的实现方式就是,我们只需要在每个切片中找到 13,就不用继续在这个切片的剩余元素中查找了。

我们用 for 经典形式来实现这个逻辑。面对这个问题,我们要使用嵌套循环,具体来说就是外层循环遍历 sl 中的元素切片,内层循环遍历每个元素切片中的整型值。一旦内层循环发现 13 这个数值,我们便要中断内层 for 循环,回到外层 for 循环继续执行。

如果我们用不带 label 的 continue 能不能完成这一功能呢?答案是不能。因为它只能中断内层循环的循环体,并继续开启内层循环的下一次迭代。而带 label 的 continue 语句是这个场景下的“最佳人选”,它会直接结束内层循环的执行,并回到外层循环继续执行。

这一行为就好比在外层循环放置并执行了一个不带 label 的 continue 语句。它会中断外层循环中当前迭代的执行,执行外层循环的后置语句(i++),然后再对外层循环的循环控制条件语句进行求值,如果为 true,就将继续执行外层循环的新一次迭代。

看到这里,一些学习过 goto 语句的同学可能就会问了,如果我把上述代码中的 continue 换成 goto 语句,是否也可以实现同样的效果?答案是否定的!一旦使用 goto 跳转,那么不管是内层循环还是外层循环都会被终结,代码将会从 outerloop 这个 label 处,开始重新执行我们的嵌套循环语句,这与带 label 的 continue 的跳转语义是完全不同的。

特别提醒,goto 是一种公认的、难于驾驭的语法元素,应用 goto 的代码可读性差、代码难于维护还易错。虽然 Go 语言保留了 goto,但不建议使用 goto 语句。

# 4.2 break 语句

有些场景中,不仅要中断当前循环体迭代的进行,还要同时彻底跳出循环,终结整个循环语句的执行。面对这样的场景,continue 语句就不再适用了,Go 语言提供了 break 语句来解决这个问题。

func main() {
    var sl = []int{5, 19, 6, 3, 8, 12}
    var firstEven int = -1

    // 找出整型切片sl中的第一个偶数
    for i := 0; i < len(sl); i++ {
        if sl[i]%2 == 0 {
            firstEven = sl[i]
            break
        }
    }
    
    println(firstEven) // 6

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

通过一个循环结构来找出切片 sl 中的第一个偶数,一旦找到就不需要继续执行后续迭代了。这个时候就通过 break 语句跳出了这个循环。

# 4.2.1 支持label

遇到嵌套循环,break 要想跳出外层循环,用不带 label 的 break 是不够,因为不带 label 的 break 仅能跳出其所在的最内层循环。要想实现外层循环的跳出,还需给 break 加上 label。

var gold = 38

func main() {
    var sl = [][]int{
        {1, 34, 26, 35, 78},
        {3, 45, 13, 24, 99},
        {101, 13, 38, 7, 127},
        {54, 27, 40, 83, 81},
    }

outerloop:
    for i := 0; i < len(sl); i++ {
        for j := 0; j < len(sl[i]); j++ {
            if sl[i][j] == gold {
                fmt.Printf("found gold at [%d, %d]\n", i, j)
                break outerloop
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

main 函数的逻辑就是,在 sl 这个二维切片中找到 38 这个数字,并输出它的位置信息。整个二维切片中至多有一个值为 38 的元素,所以只要我们通过嵌套循环发现了 38,我们就不需要继续执行这个循环了。这时,我们通过带有 label 的 break 语句,就可以直接终结外层循环,从而从复杂多层次的嵌套循环中直接跳出,避免不必要的算力资源的浪费。

gopackage main

import "fmt"

func main() {
   k := 1
   for { // 这里也等价 for ; ; {
      if k <= 10 {
         fmt.Println("ok~~", k)
      } else {
         break //break 就是跳出这个 for 循环
      }
      k++
   }
}

/*
ok~~ 1
ok~~ 2 
ok~~ 3 
ok~~ 4 
ok~~ 5 
ok~~ 6 
ok~~ 7 
ok~~ 8 
ok~~ 9 
ok~~ 10
*/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
上次更新: 2022/06/12, 15:48:09
for(循环结构)
函数基础

← for(循环结构) 函数基础→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式