if-else 结构

非常基础的三种结构

  1. if

    1. if condition {
    2. // do something
    3. }
  2. if-else

    1. if condition {
    2. // do something
    3. } else {
    4. // do something
    5. }
  3. if-else if-else

    1. if condition1 {
    2. // do something
    3. } else if condition2 {
    4. // do something else
    5. } else {
    6. // catch-all or default
    7. }
  • else-if 分支的数量是没有限制的,但是为了代码的可读性,还是不要在 if 后面加入太多的 else-if 结构。如果你必须使用这种形式,则把尽可能先满足的条件放在前面。

有用的例子

  1. 判断一个字符串是否为空:
    • if str == "" { ... }
    • if len(str) == 0 {...}
  2. 判断运行 Go 程序的操作系统类型,这可以通过常量 runtime.GOOS 来判断(第 2.2 节)。
    这段代码一般被放在 init() 函数中执行。这儿还有一段示例来演示如何根据操作系统来决定输入结束的提示:

    1. if runtime.GOOS == "windows" {
    2. . ..
    3. } else { // Unix-like
    4. . ..
    5. }
    1. var prompt = "Enter a digit, e.g. 3 "+ "or %s to quit."
    2. func init() {
    3. if runtime.GOOS == "windows" {
    4. prompt = fmt.Sprintf(prompt, "Ctrl+Z, Enter")
    5. } else { //Unix-like
    6. prompt = fmt.Sprintf(prompt, "Ctrl+D")
    7. }
    8. }
  3. 函数 Abs() 用于返回一个整型数字的绝对值:

    1. func Abs(x int) int {
    2. if x < 0 {
    3. return -x
    4. }
    5. return x
    6. }
  4. isGreater 用于比较两个整型数字的大小:

    1. func isGreater(x, y int) bool {
    2. if x > y {
    3. return true
    4. }
    5. return false
    6. }

if可以包含一个初始化语句

  1. if initialization; condition {
  2. // do something
  3. }
  • 这种写法具有固定的格式(在初始化语句后方必须加上分号)

例如:

例如:

  1. val := 10
  2. if val > max {
  3. // do something
  4. }

你也可以这样写:

  1. if val := 10; val > max {
  2. // do something
  3. }

测试多返回值函数的错误

习惯用法

  1. value, err := pack1.Function1(param1)
  2. if err != nil {
  3. fmt.Printf("An error occured in pack1.Function1 with parameter %v", param1)
  4. return err
  5. }

如:

  1. f, err := os.Open(name)
  2. if err != nil {
  3. return err
  4. }
  5. doSomething(f) // 当没有错误发生时,文件对象被传入到某个函数中
  6. doSomething

也可以将错误的获取放置在 if 语句的初始化部分:

  1. if err := file.Chmod(0664); err != nil {
  2. fmt.Println(err)
  3. return err
  4. }

或者将 ok-pattern 的获取放置在 if 语句的初始化部分,然后进行判断:

  1. if value, ok := readData(); ok {
  2. }

switch结构

表达式

  1. switch var1 {
  2. case val1:
  3. ...
  4. case val2:
  5. ...
  6. default:
  7. ...
  8. }
  • 变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。
    • 类型不被局限于常量或整数,但必须是相同的类型
    • 或者最终结果为相同类型的表达式
  • 可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3
  • 每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止
  • 还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的。
  • 可选的 default 分支可以出现在任何顺序,但最好将它放在最后。
    • 它的作用类似与 if-else 语句中的 else,表示不符合任何已给出条件时,执行相关语句。

问题5.1

  1. k := 6
  2. switch k {
  3. case 4:
  4. fmt.Println("was <= 4")
  5. fallthrough
  6. case 5:
  7. fmt.Println("was <= 5")
  8. fallthrough
  9. case 6:
  10. fmt.Println("was <= 6")
  11. fallthrough
  12. case 7:
  13. fmt.Println("was <= 7")
  14. fallthrough
  15. case 8:
  16. fmt.Println("was <= 8")
  17. fallthrough
  18. default:
  19. fmt.Println("default case")
  20. }

输出:

  1. was <= 6
  2. was <= 7
  3. was <= 8
  4. default case
  • fallthrough会让这段代码走到default分支
    • fallthrough是继续执行后续分支,default在case8后面,故而会继续执行
    • 如果我们移动default到别的地方,case8的fallthrough就会报错了,因为它没有后续分支
    • 如果我们把default放在case7后面
      此时只会返回
      1. case 6:
      2. fmt.Println("was <= 6")
      3. fallthrough
      4. case 7:
      5. fmt.Println("was <= 7")
      6. fallthrough
      7. default:
      8. fmt.Println("default case")
      9. case 8:
      10. fmt.Println("was <= 8")
      1. was <= 6
      2. was <= 7
      3. default case

练习5.2

写一个 Season 函数,要求接受一个代表月份的数字,然后返回所代表月份所在季节的名称(不用考虑月份的日期)。

  1. package main
  2. import "fmt"
  3. func Season(month int) string {
  4. switch month{
  5. case 3, 4, 5:
  6. return "Spring"
  7. case 6, 7, 8:
  8. return "Summer"
  9. case 9, 10, 11:
  10. return "Autumn"
  11. case 12, 1, 2:
  12. return "Winter"
  13. }
  14. return "Isn't month"
  15. }
  16. func main(){
  17. fmt.Println(Season(11))
  18. }

输出:

  1. Autumn

for结构

基于计数器的迭代

  1. for 初始化语句; 条件语句; 修饰语句 {}

示例:

  1. package main
  2. import "fmt"
  3. func main() {
  4. for i := 0; i < 5; i++ {
  5. fmt.Printf("This is the %d iteration\n", i)
  6. }
  7. }
  • 可以在循环中同时使用多个计数器for i, j := 0, N; i < j; i, j = i+1, j-1 {}

练习5.4

  1. 使用 for 结构创建一个简单的循环。要求循环 15 次然后使用 fmt 包来打印计数器的值。
  2. 使用 goto 语句重写循环,要求不能使用 for 关键字。
  1. package main
  2. import "fmt"
  3. func ForLoopV1(){
  4. for i := 1; i <= 15; i++ {
  5. fmt.Printf("Round %d\n", i)
  6. }
  7. }
  8. func ForloopV2(){
  9. i := 1
  10. loop2:
  11. fmt.Printf("Round %d\n", i)
  12. i++
  13. if i <= 15{
  14. goto loop2
  15. }
  16. }
  17. func main(){
  18. ForLoopV1()
  19. fmt.Println("****************")
  20. ForloopV2()
  21. }

练习5.5

创建一个程序,要求能够打印类似下面的结果(尾行达 25 个字符为止):

  1. G
  2. GG
  3. GGG
  4. GGGG
  5. GGGGG
  6. GGGGGG
  1. 使用 2 层嵌套 for 循环。
  2. 仅用 1 层 for 循环以及字符串连接。
  1. package main
  2. import "fmt"
  3. func CharLoopForOnce(c string, times int) {
  4. res := c
  5. for i := 1; i <= times; i++ {
  6. fmt.Println(res)
  7. res += c
  8. }
  9. }
  10. func CharLoopForTwice(c string, times int) {
  11. for i := 1; i <= times; i++ {
  12. for j := 1; j <= i; j++ {
  13. fmt.Printf(c)
  14. }
  15. fmt.Println()
  16. }
  17. }
  18. func main() {
  19. CharLoopForOnce("G", 25)
  20. fmt.Println("****************")
  21. CharLoopForTwice("G", 25)
  22. }

练习5.6

使用按位补码从 0 到 10,使用位表达式 %b 来格式化输出

  1. package main
  2. import "fmt"
  3. func main() {
  4. for i := 0; i <= 10; i++ {
  5. fmt.Printf("The complement of %b is: %b\n", i, ^i)
  6. }
  7. }

练习 5.7 Fizz-Buzz

写一个从 1 打印到 100 的程序,但是每当遇到 3 的倍数时,不打印相应的数字,但打印一次 “Fizz”。遇到 5 的倍数时,打印 Buzz 而不是相应的数字。对于同时为 3 和 5 的倍数的数,打印 FizzBuzz(提示:使用 switch 语句)。

  1. package main
  2. import "fmt"
  3. const (
  4. FIZZ = 3
  5. BUZZ = 5
  6. FIZZBUZZ = 15
  7. )
  8. //if也可实现
  9. func ForIF() {
  10. for i:= 1; i <= 100; i++ {
  11. if i % 3 == 0 && i % 5 == 0 {
  12. fmt.Printf("FizzBuzz\t")
  13. }else if i % 3 == 0 {
  14. fmt.Printf("Fizz\t")
  15. }else if i % 5 == 0 {
  16. fmt.Printf("Buzz\t")
  17. }else {
  18. fmt.Printf("%d\t", i)
  19. }
  20. }
  21. }
  22. func main(){
  23. for i:= 1; i <= 100; i++ {
  24. switch{
  25. case i % FIZZBUZZ == 0:
  26. fmt.Printf("FizzBuzz\t")
  27. case i % FIZZ == 0:
  28. fmt.Printf("Fizz\t")
  29. case i%BUZZ == 0:
  30. fmt.Printf("Buzz\t")
  31. default:
  32. fmt.Printf("%d\t", i)
  33. }
  34. }
  35. }

练习 5.8

  1. package main
  2. import "fmt"
  3. func main() {
  4. for i := 0; i < 10; i++ {
  5. for j := 0; j < 20; j++{
  6. fmt.Printf("*")
  7. }
  8. fmt.Println()
  9. }
  10. }

基于条件判断的迭代

基本形式为:for 条件语句 {},类似与while

  1. for i >= 0 {
  2. i = i - 1
  3. fmt.Printf("The variable i is now: %d\n", i)
  4. }

无限循环

一般情况写为for { }

  • 如果 for 循环的头部没有条件语句,那么就会认为条件永远为 true,因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。
    • 可以使用 break 语句或 return 语句直接返回

for-range 结构

一般形式为:for ix, val := range coll { }。它可以迭代任何一个集合,简单来说就是可以迭代k-v结构。

  • val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值
  • 如果 **val** 为指针,则会产生指针的拷贝,依旧可以修改集合中的原值
  1. for pos, char := range str {
  2. ...
  3. }

Break 与 continue

  • breakcontinue都是用来控制循环结构的,主要作用是停止循环。
  • break跳出一个循环体或者完全结束一个循环
  • continue跳过本次循环体中剩下尚未执行的语句,立即进行下一次的循环条件判定
    • 关键字 continue 只能被用于 for 循环中

标签与 goto

注:goto与标签并不推荐

某一行第一个以冒号(:)结尾的单词即为标签

  • for、switch 或 select 语句都可以配合标签(label)形式的标识符使用

goto会跳转到标签位置

  • 可以使用 goto 语句和标签配合使用来模拟循环
  1. package main
  2. func main() {
  3. i:=0
  4. HERE:
  5. print(i)
  6. i++
  7. if i==5 {
  8. return
  9. }
  10. goto HERE
  11. }