七叶笔记 » golang编程 » 大白话 golang 教程-05-条件和循环结构

大白话 golang 教程-05-条件和循环结构

程序代码很多时候就是对现实的表达,比如买一个东西,要看你有没有带够钱,这种逻辑关系就是判断条件。

 //CanBuy 判断钱是否足够
func CanBuy(hasMoney, productPrice int) {
  // if 后面不需要括号,if 的执行体无论是否为空 { } 都是必须的
  if hasMoney >= productPrice {
    fmt.Printf("you can, will %d left", hasMoney-productPrice)
  }
}  

if 满足的时候执行 if 的函数体,如果不满足需要执行还可以跟一个 else 语句,如果还有判断可以接 else if 语句。

 if 天不下雨 {
  // 出去吃
} else if 有肉和菜 {
  // 将就吃
} else {
  // 点外卖
}  

注意 go 语言的 if 不需要括号 (),而 if 后面的执行体总是需要括号 {},if 里可以嵌套 if,但是写代码的时候应该放置嵌套过深,if 虽然好用但是会增加代码的复杂度,下面几个函数,你觉得哪个会好一点呢?

 //CheckPrice1 判断贵不贵
func CheckPrice1(amount int) {
  if amount > 50 {
    if amount > 100 {
      fmt.Println("太贵了")
    } else {
      fmt.Println("还行")
    }
  } else {
    fmt.Println("真便宜啊")
  }
}

//CheckPrice2 判断贵不贵
func CheckPrice2(amount int) {
  if amount > 50 {
    if amount > 100 {
      fmt.Println("太贵了")
    } else {
      fmt.Println("还行")
    }
    return
  }

  fmt.Println("真便宜啊")
}

//CheckPrice3 判断贵不贵
func CheckPrice3(amount int) {
  if amount > 100 {
    fmt.Println("太贵了")
  } else if amount > 50 {
    fmt.Println("还行")
  } else {
    fmt.Println("真便宜啊")
  }
}

//CheckPrice4 判断贵不贵
func CheckPrice4(amount int) {
  if amount <= 50 {
    fmt.Println("真便宜啊")
    return 
  }

  if amount > 50 && amount <= 100 {
    fmt.Println("还行")
  } else {
    fmt.Println("太贵了")
  }
}

//CheckPrice5 判断贵不贵
func CheckPrice5(amount int) {
  if amount > 100 {
    fmt.Println("太贵了")
    return
  }

  if amount > 50 {
    fmt.Println("还行")
    return
  }

  fmt.Println("真便宜啊")
}  

你可能学过 C 语言的三目运算符,觉得它们看起来都不太好,不过 go 不支持三目运算符,因为三目运算符能写出非常复杂的代码。就目前学习的知识点来看,版本 checkPrice5 相对来讲没有过多的嵌套的层级,使用 if 的时候要特别注意这个代码复杂度的问题。

除了判断,就是循环,for 循环可以指定执行的次数,完整的 for 循环是:

 // FullFor 完整的 for 定义
func FullFor() {
  sum := 0

  // 初始值; 循环条件; 增量/减量
  for i := 0; i < 10; i++ {
    sum += i
  }

  fmt.Println(sum)
}  

初始值和增减量是可以没有,这时候两边的分号也可以不写了。

 // ConditionFor 只有循环条件
func ConditionFor() {
  sum := 1
  // for ; sum < 10 ; {
  for sum < 10 {
    sum += sum
  }

  fmt.Println(sum)
}  

如果连循环条件也没有,那就是死循环了,一直出不来,你需要用 ctrl + c 或者 kill 来结束掉进程,在一个中如果出现死循环往往是致命的,可能导致 CPU 超级高,但是程序本身并没有做什么有意义的事情。

 // DeadFor 死循环
func DeadFor() {
  for true {
    // 无限循环
  }

  for {
    // 无限循环
  }
}  

for 循环的过程,可以使用 continue 来跳过当次循环,使用 break 中断循环。

 //BreakContinueFor 跳过和中断
func BreakContinueFor() {
  for i := 0; i < 100; i++ {
    // 余数是 0 是偶数,跳过
    if i%2 == 0 {
      continue
    }

    // 大于 10 就中断循环
    if i > 10 {
      break
    }

    fmt.Printf("%d ", i)
  }
  fmt.Println()
}  

for-range 用来迭代字符串、数组、切片等类型,index 是迭代的索引,类似循环变量,ch 是拷贝的迭代值,for-range 有一些要注意的知识点和坑,以后会经常用到它。

 // ForRange 安全的迭代循环
func ForRange() {
  str := "this is a test"
  for index, ch := range str {
    // 跳过空格不处理
    if ch != 0x20 {
      fmt.Printf("%d%c ", index, ch)
    }
  }
  fmt.Println()
}  

如果不关心循环的具体值,可以不写 for-range 的第二个循环变量。

 // ForRange2 安全的迭代循环
func ForRange2() {
  str := "this is a test"
  for index := range str {
    fmt.Printf("%d ", index)
  }
  fmt.Println()
}  

如果不关心循环的次数,index 却不能不写,但是可以使用 _ 占位符来代替,表示不关心它具体的值。

 // ForRange3 安全的迭代循环
func ForRange3() {
  str := "this is a test"
  for _, ch := range str {
    fmt.Printf("%c", ch)
  }
  fmt.Println()
}  

switch-case 用于不同的条件执行不同的动作,每个 case 都是一个分支,挨个比对值,如果匹配就执行对应的分支,后面的分支不再测试,注意 case 分支不需要加 break。

 // FullSwitch 挨个顺序匹配
func FullSwitch(rank string) {
  switch rank {
  case "gold":
    fmt.Println("1st")
  case "silver", "bronze":
    fmt.Println("2nd or 3rd")
  default:
    // 匹配不到才会执行
    fmt.Println("未获奖")
  }
}  

switch-case 有个特殊的 fallthrough 用法,它会无条件的执行匹配到的下一个case。

 // FallThrough 执行匹配到下一个 case 语句(无论它的条件是真是假)
func FallThrough() {
  switch {
  case true:
    fmt.Println("我是匹配到的")
    fallthrough
  case false:
    fmt.Println("虽然我是 false,但我会被打印")
    fallthrough
  case false:
    fmt.Println("虽然我也是 false,但我也会被打印")
    // fallthrough
    // 如果放开这个 fallthrough,下面的 default 也会被执行
  default:
    fmt.Println("放开上面的 fallthrough 才能看到我")
  }
}  

goto 语句可能被认为是洪水猛兽,因为滥用 goto 可能是程序的逻辑变得混乱,go 语言支持 goto 语句,需要一个 label 来配合申明,下面的代码使用 goto 跳出 3 层 for 循环,如果使用 if + break 需要做标记变量层层判断(break 不能中断外层的循环体),使用 goto 简化了代码复杂度。

 //JumpOutside 使用 goto 跳出多层循环
func JumpOutside() {
  for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
      for k := 0; k < 10; k++ {
        fmt.Println(i, j, k)
        if i == 1 && j == 2 && k == 3 {
          goto GAMEOVER
        }
      }
    }
  }

GAMEOVER:
  fmt.Println("game over")
}  

select 分支语句是 go 语言非常重要的分支判断语句,在学了通道以后会用的特别多,也是有别于其他语言的一个语法。select 带有多个 case,每个 case 都是一个 IO 操作,select 随机的选择满足条件的执行一个,重点来了,如果没有 case 可以运行,它将发生阻塞,一直等到有一个 case 满足条件位置。所以下面的代码是一个死循环,一直会阻塞,因为它没有 case,永远都不会满足一个。

 // DeadSelect 一直阻塞
func DeadSelect() {
  select {}
}  

由于我们还没有学习关于通道的知识,本节暂时略过 select 语句,它非常重要,在学习通道的时候会详细讲解它的用法。

本章节的代码

相关文章