跳至主要內容

结构化编程

LincZero大约 6 分钟

结构化编程

流程控制

和其他编程语言一样

条件判断语句

if

与其他语言不同

  • go的条件表达式可以也建议不写(),这一点和python一样
  • 哪怕只有一行代码也要写 {}
if 条件表达式 {
	// ...
}

// 例如
var count int = 20
if count < 30 {
    fmt.Println("数量不足")
}

// go支持在if后定义变量 (怪怪的)
if count := 20; count < 30 {
    // ...
}

else

与其他语言不同

  • 该写法是错误的:

    if 条件表达式 {
        逻辑代码1
    } // 即这里不能加换行。我个人不是很习惯这个,因为我有时会喜欢在这个位置加独立一行的注释
    else {
        逻辑代码2
    }
    
if 条件表达式 {
    逻辑代码1
} else {
    逻辑代码2
}

// 例如
var count int = 20
if count < 30 {
    fmt.Println("数量不足")
} else {
    fmt.Println("数量足够")
}

else if

if 条件1 {
    // ...
} else if 条件2 {
    // ...
} else if 条件3 {
    // ...
} else {
    // ...
}

switch case

switch 表达式 {
    case1,2, ...:
    	// ...
    case3,4, ...:
    	// ...
    default:
    	// ...
}

细节:

  • switch:后面可以是:

    • 一个表达式

    • 也可以不带表达式,当作if分支使用。歪门邪道,看起来其实比 "else-if" 语句更整齐更舒服,但目前不推荐使用

      var a int32 = 2
      switch {
          case a == 1:
          	// ...
          case a == 2:
          	// ...
      }
      
    • 后面可以直接声明/定义一个变量,分号结束。歪门邪道,不推荐

      switch b := 7; {
          case b > 6:
      	    fmt.Println("大于6")
          case b <= 6:
          	fmt.Println("不大于6")
      }
      
  • case:

    • 后面可以是:
      • 常量值,要求不能重复
      • 变量值,要求和switch表达式数据类型一致 (哈? 能变量? 其他语言可以放可变变量么)
      • switch后面不带表达式,这里就类似if语句,可以写表达式
  • default:不是必须,位置随意

与其他语言不同:go 的 switch穿透问题

  • 后面不需要带 break 如果要做到类似其他语言的效果,要在case语句块后增加fallthrough关键字,则会继续执行下一个case。叫switch穿透
  • 可以带多个值,使用逗号间隔 (是 case v1, v2,而非 case v1: case v2:)。不同与其他语言的写法,也可归功于是上一点的原因
  • (假设没其他语言的影响,我较认可这个语法。毕竟大部分场景下还是switch不穿透用得比较多)
Type Switch (switch + 类型断言)

Type Switch:switch + 类型断言,一种固定的特殊语法。详见 断言 一章

switch s.(type){
	case Chniese:
    	...
	case American:
    	...
    default:
    	...
}

循环结构

与其他语言不同:go只有for循环,没有 while 和 do while 循环。作为代替用的是 for {} 表示死循环。话说这个还比较符合我个人习惯:

  1. 我一直觉得死循环完全能代替while和do-while,且语句更统一
  2. 哪怕有需要,也就是编译器优化,自动检查死循环里第和最后一个判断语句是否为纯break操作的事而已。类似于尾递归或尾返回优化
  3. 在多break循环语句中,我个人会更倾向于写 while(true),再把对应的判断放在第一个if语句里,这样会更好追踪或打印while的退出原因)
  4. 总结下来,优点是语法统一,缺点是对于特定的一些单break的简单循环语句,可能要多写三行代码。比较想吐槽的是虽然少了while和do while,但死循环有两种写法……你这语法数量压根没少啊……golang的简洁设计有时一点都不简洁……简洁还得看python,只有for in

for

// 语法
for (初始表达式; 布尔表达式; 迭代因子) {
    循环体;
}

// 举例1:
var sum int = 0
for var i int = 1; i <= 5; i++ {	// 错误,这里不能用var,必须用:=的方式定义
    sum += 1
}

// 举例2:
var sum int = 0
for i := 1; i <= 5; i++ {			// 正确
    sum += 1
}

死循环 (伪while)

死循环:有两种写法

// 语法1
for {
    // ...
}

// 语法2
for ;; {
    // ...
}

for range 键值循环

可以遍历 数组、切片、字符串、map、通道

// 语法
for key, val := range coll {
    // ...
}
按字节与按字符遍历
// 例如
var str string = "hello golang你好"
// 方式1 - 按字节遍历
for i := 0; i < len; i++ {
    fmt.Printf("索引 %d, 值 %c", i, value)		// ..., 11 g, 12 乱码, 13 乱码, 14 乱码, ...
}
// 方式2 - 按字符遍历  (这里自动按utf-8分割,例如遇到汉字字符则会占3Byte)
for i, value := range str {
    fmt.Printf("索引 %d, 值 %c", i, value)		// ..., 11 g, 12 你, 15 号
}

break、continue、goto、return

这几个基本和其他语言里的使用是一样的

  • break:提前结束一层循环。与其他语言不同的是在switch-case语句中无需使用,break仅在循环中使用
  • continue:结束本次循环
  • goto:无条件跳转到指定位置
  • return:立刻返回函数

标签

break 标签

// 双重循环
lable2:						// 为外层循环打标签
for i:=1; i<=5; i++ {
    for j:=2; j<=4; j++ {
        fmt.Printf("i: %v, j: %v \n", i, j)
        if i == 2 && j == 2 {
            break lable2	// 结束指定标签的循环,这里会退出整个外层循环,否则是退出内部循环。可用goto到循环尾代替
        }
    }
}

continue 标签

// 双重循环
lable2:						// 为外层循环打标签
for i:=1; i<=5; i++ {
    for j:=2; j<=4; j++ {
        if i==2 && j==2 {
			continue lable2	// 结束指定标签的本轮循环。可用goto到循环头代替
        }
        fmt.Printf("i: %v, j: %v \n", i, j)
    }
}

goto 标签

goto 语句在高级语言中用得比较少,主要在汇编中使用。作用是无条件转移到程序中指定行

C/C++/Java 等语言中也有 goto 语句,但学校和网课一般不教,也一般不建议用,避免造成流程混乱

fmt.Println("hello world1")
fmt.Println("hello world2")
if 1 == 1 {
	goto label8	// 一般配合条件语句使用
}
fmt.Println("hello world3")
fmt.Println("hello world5")
fmt.Println("hello world6")
fmt.Println("hello world7")
label8:
fmt.Println("hello world8")
  • break 标签,这个用法等同 goto 到循环尾
  • continue 标签,这个用法等同 goto 到循环头
  • goto 标签,同其他语言

笔者:

与其他语言不同:break 标签,和 continue 标签,应该是其他语言没有的,这两个语句完全可以用goto代替。

设计目的估计是:尽管条件+循环的用于规范了流程控制,但仍不如goto灵活,极端情况下可能会出现使用goto更方便的情景。而增加了break标签和continue标签两种新的流程控制方法外,可以进一步压缩goto的使用场景。

缺点:从语法简洁性上,我觉得这两种语句不如 goto 语句单一和简明 优点:提供了更严格的语法限制,新的关键字可能能方便编译器进行检查优化 要我设计的话:给编辑器检查添加 goto 的使用警告。但如果标签仅存在循环头或循环尾且该标签的 goto 语句在该循环内,则取消警告