结构化编程
结构化编程
流程控制
和其他编程语言一样
条件判断语句
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 表达式 {
case 值1, 值2, ...:
// ...
case 值3, 值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 {}
表示死循环。话说这个还比较符合我个人习惯:
- 我一直觉得死循环完全能代替while和do-while,且语句更统一
- 哪怕有需要,也就是编译器优化,自动检查死循环里第和最后一个判断语句是否为纯break操作的事而已。类似于尾递归或尾返回优化
- 在多break循环语句中,我个人会更倾向于写 while(true),再把对应的判断放在第一个if语句里,这样会更好追踪或打印while的退出原因)
- 总结下来,优点是语法统一,缺点是对于特定的一些单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 语句在该循环内,则取消警告