【Golang入门】九、程序流程控制

程序中,程序运行的流程控制决定程序如何执行,包含顺序控制分支控制循环控制三类。

一、顺序控制

程序从上到下逐行执行,中间没有任何判断和跳转。

二、分支控制 if-else

分支控制就是让程序有选择的执行,包含单分支双分支多分支

1. 单分支

基本语法为:条件表达式可以加括号(),但是官方不推荐!

// 条件表达式为真时,执行{}中的代码
// {}必须有
if 条件表达式 {
    执行代码块
}

示例:编写一个程序,输入人的年龄,如果年龄大于18岁,则输出“你成年了!”。

package main

import (
  "fmt"
)
func main() {
  var age int
  fmt.Println("请输入年龄: ")
  fmt.Scanln(&age)
  
  // if 的条件判断语句里允许声明一个变量,该变量的作用域只能在该条件逻辑块内
  // if age := 20; age > 18 {
  //     执行代码块
  // }
  if age > 18 {
    fmt.Println("你成年了!")
  }
}
1) 使用细节

Golang中,if 的条件判断语句里允许声明一个变量,该变量的作用域只能在该条件逻辑块内。注意:{} 必须要!!!

if age := 20; age > 18 {
   fmt.Println("你成年了!")
}

2. 双分支

基本语法为:

// 条件表达式为真时,执行{}中的代码
// {}必须有
if 条件表达式 {
    执行代码块1
} else {
    执行代码块2
}

改进上面的代码,条件 age > 18 不成立时,输出"你还是个小屁孩!":

package main

import (
  "fmt"
)
func main() {
  var age int
  fmt.Println("请输入年龄: ")
  fmt.Scanln(&age)
  
  if age > 18 {
    fmt.Println("你成年了!")
  } else {
    fmt.Println("你还是个小屁孩!")
  }
}

3. 多分支

基本语法为:

// 条件表达式为真时,执行{}中的代码
// {}必须有
if 条件表达式1 {
    执行代码块1
} else if 条件表达式2 {
    执行代码块2
} else if 条件表达式3 {
    执行代码块3
} ···

} else {
    执行代码块n
}

示例:求 a x 2 + b x + c = 0 ax^2 + bx + c = 0 ax2+bx+c=0方程的根。

package main

import (
  "fmt"
  "math"
)
func main() {
  var a, b, c float64
  var x1, x2, x float64
  fmt.Println("请输入a, b, c: ")
  fmt.Scanf("%f %f %f", &a, &b, &c)
  if (b * b - 4 * a * c) > 0 {
    x1 = (-b + math.Sqrt(b * b - 4 * a * c)) / (2 * a)
    x2 = (-b - math.Sqrt(b * b - 4 * a * c)) / (2 * a)
    fmt.Println("该方程有两个解, 解为 x1 = ", x1, "x2 = ", x2)
  } else if (b * b - 4 * a * c) == 0 {
    x = -b / (2 * a)
    fmt.Println("该方程仅有一个解, 解为 x = ", x)
  } else {
    fmt.Println("该方程无解!")
  }
}

输出结果为:

注意:嵌套分支不宜太多,尽量控制在三层以内。

三、switch 分支结构

1. 语法结构

Golang中,匹配项后面不需要加break,默认执行完其中一个 case 或 default 之后结束 switch。基本语法为:

switch 表达式 {
// case 后的表达式可以有多个,使用逗号隔开
// 表达式 == 表达式1 || 表达式 == 表达式2 || ···
// 则执行语句块1
case 表达式1, 表达式2, ... : 
      语句块1
case 表达式3, 表达式4, ... :
      语句块2
default:
      语句块
}

switch 执行的流程: 先执行表达式,得到值,然后和 case 的表达式进行比较,若相等,则完成匹配并执行对应的 case 的语句块,然后结束 switch。

package main

import (
  "fmt"
  _ "math"
)
func main() {
  var ch byte
  fmt.Println("请输入一个字符 a-g: ")
  fmt.Scanf("%c", &ch)

  switch ch {
  case 'a' :
    fmt.Println("星期一") // ch == 'a',执行 case 'a'。
  case 'b' :
    fmt.Println("星期二")
  case 'c' :
    fmt.Println("星期三")
  case 'd' :
    fmt.Println("星期四")
  case 'e' :
    fmt.Println("星期五")
  case 'f' :
    fmt.Println("星期六")
  case 'g' :
    fmt.Println("星期天")
  default :
    fmt.Println("错误!!!")
  }
}

输出结果为:

2. 使用细节

1) case

1、case 后面是一个表达式:常量、变量、有返回值的函数等;
2、case后表达式的值的数据类型要和switch的表达式的数据类型一致
3、case 后面不加break;
4、case后面的表达式如果是常量,要求不能重复

2) switch

1、switch后面也可以不带表达式,类似于if-else分支

var age int = 10
switch {
    case age == 10: // 表达式也可以为:age > 5 && age < 15,age > 5 等。
        语句块1
    case age == 20:
        语句块2
    default:
        语句块3
}

2、switch后面也可以直接 声明/定义 一个变量 ( switch age := 20; {} ),以分号结束,但是不推荐!!!
3、switch穿透:fallthrough,case 语句块后增加fallthrough,继续执行下一个case

package main

import (
  "fmt"
  _ "math"
)
func main() {
  var age int = 20
  switch {
  case age > 5 && age < 25:
    fmt.Println("年龄处于范围内!")
    fallthrough
  case age == 50:
    fmt.Println("age = ", age)
  default:
    fmt.Println("哦豁, 穿透失败! 检查是否添加关键字fallthrough!")
  }
}

输出结果为:

注意穿透的有趣现象:穿透之后的下一个 case 表达式仿佛失效了!

4、type switch:用于判断某个 interface (接口) 变量实际指向的变量类型

3. 练习

1) 将小写类型的char型转换为大写,只转换a, b, c, d, e,其他的输出other。
package main

import (
  "fmt"
)
func main() {
  var ch byte
  fmt.Println("请输入小写字母:")
  fmt.Scanf("%c", &ch)
  switch ch {
  case 'a', 'b', 'c', 'd', 'e':
    fmt.Printf("%c 的大写字母是 %c", ch, ch - 32)
  default:
    fmt.Println("other!")
  }
}

输出结果为:

2) 成绩>60,输出“合格”;<60,输出“不合格”。输入的成绩不能大于100。
package main

import (
  "fmt"
)
func main() {
  var score int
  fmt.Println("请输入分数:")
  fmt.Scanln(&score)
  /*******************************
  switch int(score / 60) {
  case 1:
    fmt.Println("合格")
  case 0:
    fmt.Println("不合格")
  default:
    fmt.Println("输入成绩大于100!!!")
  }
  ********************************/
  switch {
  case score > 60 && score <= 100:
    fmt.Println("合格")
  case score < 60:
    fmt.Println("不合格")
  default:
    fmt.Println("输入成绩大于100!!!")
  }
}

输出结果为:

4. switch和if的比较

1) 什么情况下使用 switch

判断的具体数值不多,且符合整数、浮点数、字符、字符串这几种类型。

2) 什么情况下使用 if

区间判断和结果为bool类型的判断,使用 if

四、循环控制

1. for 循环

1) 语法结构

基本语法结构为:

for 循环变量初始化; 循环条件; 循环变量迭代 {
    循环操作语句
}
// for example
for i := 1; i <= 10; i++ {
    fmt.Println("Li")
}

四要素: 循环变量初始化;循环条件;循环操作语句(循环体);循环变量迭代。

2) 使用细节

1、循环条件是一个返回bool值的表达式;
2、for循环的第二种使用方式:将变量初始化和变量迭代写到其他位置。

循环变量初始化
for 循环条件 {
    // 循环操作语句
    循环变量迭代
}
// for example
i := 1
for i <= 10 {
    fmt.Println("Li")
    i++
}

3、for 循环的第三种使用方式

for { // 等价于 for ; ; { 
    // 循环体
}

等价于 for ; ; {},是一个无限循环,通常配合break使用。

4、Golang提供for-range方法,方便遍历字符串和数组:

// 使用for-range遍历字符串
package main

import (
  "fmt"
)
func main() {
  str := "hello, Chongqing"
  for index, val := range str { 
    fmt.Printf("index = %d, val = %c\n", index, val)
  }
}
// 传统遍历方式
str := "hello, Chongqing"
for index := 1; index <= len(str); index++ {
    fmt.Printf("index = %d, val = %c\n", index, str[index - 1])
}

for-range 的输出结果去下图所示,其中 index 保存 val 对应的下标

注意:

  1. 如果字符串 str 中含有中文传统的遍历方式会出现乱码错误,因为传统方式按照字节来遍历字符串Golang中使用 UTF-8 编码,一个英文字符对应1个字节,一个中文字符对应 3 个字节,因此出现乱码。 解决方法:将 str 转成 [ ]rune(str) 切片,即 str2 = [ ]rune(str)。

  2. for-range按照字符来遍历,字符串有中文也不会出现乱码。

2. while和do-while的实现

Golang 中没有 while 和 do-while 语法,可以通过 for 循环实现

1) for循环实现while循环
// 使用for循环实现while循环
循环变量初始化
for {
    if 循环条件表达式 {
        break  // 跳出for循环
    }
    循环体
    循环变量迭代
}

注意:

  1. for {} 循环是无限循环;
  2. break 语句的作用是跳出 for 循环。
2) for循环实现do-while循环
// 使用 for 循环实现 do-while 循环
循环变量初始化
for {
    循环体
    循环变量迭代
    if 循环条件表达式 {
        break  // 跳出for循环
    }
}

注意:

  1. 先执行,再判断,至少执行一次;
  2. break 语句的作用是跳出 for 循环。

3. 循环嵌套 (多重循环)

1) 案例一

统计3个班的成绩情况,每个班有5名学生,求出各个班的平均分和所有班的平均分,从键盘输入学生成绩。

package main

import (
  "fmt"
)
func main() {
    var classNum int = 3
    var stuNum int = 5
    var total_sum float64 = 0.0
    for j:= 1; j <= classNum; j++{
        var sum float64 = 0.0
        for i := 1; i <= stuNum; i++ {
            var score float64
            fmt.Printf("请输入第%d个班的第%d个学生的成绩: ", j, i)
            fmt.Scanln(&score)
            sum += score
        }
        fmt.Printf("第%d个班级的平均分为: %v \n", j, sum / float64(stuNum))
        total_sum += sum
    }
    fmt.Printf("所有班级的平均分为: %v \n", total_sum / float64(stuNum * classNum))
}

输出结果为:

2) 案例2 打印金字塔

编写一个程序,接收一个整数表示层数,打印出金字塔。

package main

import (
  "fmt"
)
func main() {
  // 打印金字塔
  /****************
      *
     ***
    *****
  *****************/
  var totalLevel int = 3
  // i 表示层数
  for i := 1; i <= totalLevel; i++ {
    // k 表示每层打印多少空格
    for k := 1; k <= totalLevel - i; k++ {
      fmt.Print(" ")
    }
    // j 表示每层打印多少 *
    for j := 1; j <= (2 * i - 1); j++ {
      fmt.Print("*")
    }
    fmt.Println()  // 换行
  }
}

输出结果为:

打印空心金字塔。

package main

import (
  "fmt"
)
func main() {
  // 5. 打印空心金字塔
  /****************
      *
     * *
    *****
  *****************/

  var totalLevel int = 3
  // i 表示层数
  for i := 1; i <= totalLevel; i++ {
    // k 表示每层打印多少空格
    for k := 1; k <= totalLevel - i; k++ {
      fmt.Print(" ")
    }
    // j 表示每层打印多少 *
    if i != totalLevel{
      for j := 1; j <= (2 * i - 1); j++ {
        if j == 1 || j == (2 * i - 1) {
          fmt.Print("*")
        } else {
          fmt.Print(" ")
        }
      }
    } else {
      for j := 1; j <= (2 * i - 1); j++ {
          fmt.Print("*")
      }
    }
    fmt.Println()  // 换行
  }
}

输出结果为:

五、跳转控制语句

1. break

break 用于终止某个语句块的执行,中断当前 for 循环或跳出 switch 语句。

随机生成1-100之间的整数,直到生成99时停止,记录生成了多少次。

package main

import (
  "fmt"
  "math/rand"
  "time"
)
func main() {
  // 生成随机种子
  // time.Now().Unix():返回一个从1970/01/01 00:00:00 到现在的秒数
  rand.Seed(time.Now().Unix())
  // 随机生成1-100的整数
  var n int
  count := 0
  for {
    // rand.Seed(time.Now().UnixNano())
    n = rand.Intn(100) + 1
    count++
    if n == 99 {
      break
    }
  }
  fmt.Println(n, count)
}

输出结果:

代码解释:

  1. 随机生成 0-100 之间的整数,调用 “math/rand” 包中的 Intn 函数;
  2. time 包中的 time.Now().Unix():返回一个从 1970/01/01 00:00:00 到现在的秒数。
使用细节

1、 break 语句出现在多层嵌套的语句块中,可通过标签(可以自定义名称)指明要终止的是哪一层语句块

package main

import (
  "fmt"
)
func main() {
  var totalLevel int = 9
  // i 表示层数
  label1:
  for i := 1; i <= totalLevel; i++ {
    // j 表示每行的乘法算式
    // label2
    for j := 1; j <= i; j++ {
      if j == 9 {
        // break label2
        break label1
      }
      fmt.Printf("%d * %d = %d \t", i, j, i * j)
    }
    fmt.Println()
  }
}

2. continue

continue 用于结束本次循环,直接跳转到循环变量迭代,继续执行下一次循环。同样地,continue语句出现在多层嵌套的语句块中,可通过标签(可以自定义名称)指明要跳过的是哪一层语句块

3. goto (不主张使用)

goto 可以无条件转移到程序中指定的行。

// 基本语法
goto label
...
label: statement
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值