《10节课学会Golang-04-流程控制》

项目地址:github.com/Zhouchaowen/golang-tuto...
视频地址:b站 10节课学会Golang,Go快速入门

流程控制

循环语句

Golang中有三种类型的循环语句:for 循环、range 循环和 goto 语句。

  • For循环

Golang中通过For关键字来定义一个循环并且只有For关键字(Golang中没有while关键字),格式

for initialization; condition; post { // do something } 其中,initialization 是循环开始前的初始化语句,condition 是循环条件,post 是每次循环结束后执行的语句。这些语句都是可选的,如果不需要可以省略。

package main

import "fmt"

// Steps1 通过 for 循环累加 0-9
func Steps1() {
    sum := 0
    // for 循环
    // i := 0 初始化语句:在第一次迭代前执行
    // i < 10 条件表达式:在每次迭代前求值
    // i++    后置语句:在每次迭代的结尾执行
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Printf("\tsum: %d\n", sum)
}

func main() {
    fmt.Println("Steps1():")
    Steps1()
}

通过For实现类似while的语义

package main

import "fmt"

// Steps2 for循环初始化语句和后置语句不是必须的
func Steps2() {
    sum := 0
    // 初始化语句和后置语句是可选的
    for sum < 5 {
        sum++
    }
    fmt.Printf("\tsum: %d\n", sum)
}

func main() {
    fmt.Println("Steps2():")
    Steps2()
}
  • Range

通过Range关键字来遍历字符串,数组,切片或映射

package main

import "fmt"

// Steps3 range形式的循环遍历
func Steps3() {
    str := "Golang Tutorial"
    for i, v := range str { // 遍历字符串
        fmt.Printf("\ti:%d,v:%c\n", i, v)
    }
}

func main() {
    fmt.Println("Steps3():")
    Steps3()
}

Rangefor遍历的区别

package main

import "fmt"

// Steps4 range和for遍历的区别
func Steps4() {
    str := "Golang 教程"
    for i := 0; i < len(str); i++ {
        fmt.Printf("\ti:%d,v:%c\n", i, str[i])
    }

    for i, v := range str { // 遍历字符串
        fmt.Printf("\ti:%d,v:%c\n", i, v)
    }
}

func main() {
    fmt.Println("Steps4():")
    Steps4()
}

For循环中的breakcontinue

package main

import "fmt"

// Steps5 for 循环中的 break 和 continue
func Steps5() {
    for i := 0; i < 10; i++ {
        if i == 5 { // 下一小节介绍
            fmt.Printf("\ti:%d, continue\n", i)
            continue
        }

        if i == 6 {
            fmt.Printf("\ti:%d, break\n", i)
            break
        }
    }
}

func main() {
    fmt.Println("Steps5():")
    Steps5()
}
  • Goto实现循环
package main

import "fmt"

// Steps6 goto 实现循环
func Steps6() {
    i := 0

Next: // 跳转标签声明
    fmt.Printf("\ti:%d\n", i)
    i++
    if i < 5 {
        goto Next // 跳转
    }
}

func main() {
    fmt.Println("Steps6():")
    Steps6()
}

goto 语句用于无条件跳转到程序的另一个位置。其中,Next 是一个标识符,用于指定要跳转到的位置。注意,Next 必须在当前函数内部定义。

If判断

GolangIf语句和其它语言语义相同

package main

import "fmt"

// if 分支打印不同字符
func main() {
    flag := 10
    if flag > 5 { // 判断表达式
        fmt.Println("flag:", flag)
    }

    flag = 14
    //flag = 16
    //flag = 21

    if flag > 20 {
        fmt.Println("flag:", flag)
    } else if flag < 15 {
        fmt.Println("flag:", flag)
    } else {
        fmt.Println("flag:", flag)
    }
}

Switch选择

Golang中可以通过switch-case来实现分支选择, 每一个case分支都是唯一的,从上往下逐一判断,直到匹配为止,如果某些case分支条件重复了,编译会报错。

每个case分支最后自带break效果,匹配成功就不会执行其它case; 如果所有分支都没有匹配成功并且又定义了default分支, 那最终会走default分支。

case 后面的值可以是任何常量表达式,例如字符串、数字、布尔值等等。

示例一:

package main

import "fmt"

// Steps1 基础用法
func Steps1() {
    flag := 1
    //flag = 2
    //flag = 3
    //flag = 4
    //flag = 5

    switch flag { // flag 待判断条件
    case 1: // 条件 flag 是否等于 1。是:执行该case下的流程,否:选择其它满足条件的 case
        fmt.Println("\tcase:", flag)
        // Golang 中每个 case 后面不需要 break 语句。当然 return 是可选的
    case 2:
        fmt.Println("\tcase:", flag)
    case 3, 4: // case 可以设置多个条件。只要 flag 等于3或4都能执行当前case流程
        fmt.Println("\tcase:", flag)
    case 5:
        fmt.Println("\tcase:", flag)
        return
    default: // 当所有case都无法满足, 会执行 default 的流程。如果没有 default 那当前 switch 执行完成
        fmt.Println("\tdefault:", flag)
    }
}

// Steps2 switch 条件可以是任何支持判断的类型
func Steps2() {
    flag := "Hello"
    flag = "World"
    flag = "Golang"
    flag = "Tutorial"
    flag = "Process"

    switch flag { // flag 待判断条件
    case "Hello": // 条件 flag 是否等于 "Hello"。是:执行该case下的流程,否:选择其它满足条件的 case
        fmt.Println("\tcase:", flag)
    case "World":
        fmt.Println("\tcase:", flag)
    case "Golang", "Tutorial": // case 可以设置多个条件。只要 flag 等于"Golang"或"tutorial"都能执行当前case流程
        fmt.Println("\tcase:", flag)
    default: // 当所有case都无法满足, 会执行 default 的流程。如果没有 default 那当前 switch 执行完成
        fmt.Println("\tdefault:", flag)
    }
}

// switch 是编写一连串 if - else 语句的简便方法
func main() {
    fmt.Println("Steps1():")
    Steps1()
    fmt.Println("Steps2():")
    Steps2()
}

示例二:

package main

import "fmt"

// Steps3 switch true 可以将一长串 if-then-else 写得更加清晰
func Steps3() {
    flag := 1
    //flag = 2
    //flag = 3
    //flag = 4
    //flag = 5
    //flag = 7

    switch { // flag 待判断条件
    case flag < 2: // 条件 flag 是否小于 2。是:执行该case下的流程,否:选择其它满足条件的 case
        fmt.Println("\tcase flag < 2 flag:", flag)
    case flag < 4:
        fmt.Println("\tcase flag < 4, flag:", flag)
    case flag > 6, flag < 10: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
        fmt.Println("\tcase flag > 6 || flag < 10 flag:", flag)
    case flag > 6 && flag < 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
        fmt.Println("\tcase flag > 6 || flag < 10 flag:", flag)
    }
}

// Steps4 for + switch 的使用
func Steps4() {
    for flag := 0; flag < 11; flag++ {
        switch { // flag 待判断条件
        case flag < 2: // 条件 flag 是否小于 2。是:执行该case下的流程,否:选择其它满足条件的 case
            fmt.Println("\tcase flag < 2 flag:", flag)
        case flag < 4:
            fmt.Println("\tcase flag < 4, flag:", flag)
        case flag > 6, flag < 8: // case 可以设置多个条件。flag 大于6或小于10都能执行当前case流程
            fmt.Println("\tcase flag > 6 || flag < 8 flag:", flag)
        case flag > 6 && flag < 10: // case 可以设置组合条件。flag 大于6并且小于10都才能执行当前case流程
            fmt.Println("\tcase flag > 6 && flag < 10 flag:", flag)
        }
    }
}

// switch 是编写一连串 if - else 语句的简便方法
func main() {
    fmt.Println("Steps3():")
    Steps3()
    fmt.Println("Steps4():")
    Steps4()
}

Defer

Golang中通过defer来实现延时调用, 用于指定一个函数调用在函数返回之前执行。常用来做一些收尾工作: 关闭连接,清理资源

package main

import "fmt"

// defer作用:
//    释放占用的资源
//    捕捉处理异常 recover

// Steps1 defer 语句会将函数推迟到外层函数返回之后执行。
// 推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
func Steps1() {
    defer fmt.Printf(" world\n")

    fmt.Printf("\thello")
}

func main() {
    fmt.Println("Steps1():")
    Steps1()
}

在上面的示例中,defer 语句都在函数中定义,它们会在函数返回之前执行。defer fmt.Printf(" world\n"),会在func Steps1()返回前执行。

通过defer延时打印数字:

package main

import "fmt"

// Steps2 推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
func Steps2() {
    fmt.Println("\tbegin")
    for i := 0; i < 3; i++ {
        defer fmt.Println("\t\ti:", i)
        fmt.Printf("\t\ti:%d\n", i)
    }
    fmt.Println("\tend")
}

func main() {
    fmt.Println("Steps2():")
    Steps2()
}

/*
   ----- -----
  |   | |    |
  | | V | |  |
  |    |     |  V
    | ... |
    |  3  |
    |  2  |
    |  1  |
    |  0  |
     —————
*/
/* 执行结果
begin
    i:0
    i:1
    i:2
end
    i: 2
    i: 1
    i: 0
*/

defer 语句中引用函数中的变量,会在函数调用是根据最新的值计算(Steps3); defer 语句中的函数参数会在 defer 语句定义时计算,而不是在函数调用时计算(Steps4)。

package main

import "fmt"

func Steps3() {
    fmt.Println("\tbegin")
    x := 2
    defer func() {
        x = x * x
        fmt.Println("\tx =", x) // x = 9
    }()
    fmt.Println("\tend")
    x = 3
}

func Steps4() {
    fmt.Println("\tbegin")
    x := 2
    defer func(x int) {
        x = x * x
        fmt.Println("\tx =", x) // x = 4
    }(x)
    fmt.Println("\tend")
    x = 3
}

func main() {
    fmt.Println("Steps3():")
    Steps3()
    fmt.Println("Steps4():")
    Steps4()
}

recover

recoverGo语言中用于从 panic 恢复的内置函数。

当函数中发生 panic 时,程序会停止执行当前函数的代码,但是会继续执行当前函数的 defer 语句,直到所有的 defer 语句都执行完毕。如果其中某个 defer 语句调用了 recover,则程序会停止向上传递 panic,并在调用 recover 的地方继续执行代码,而不是终止程序的运行。

package main

import "fmt"

// 捕捉处理异常 recover
func main() {
    defer func() {
        if err := recover(); err != nil {
            // 捕捉错误 run err: runtime error: integer divide by zero
            fmt.Println("run err:", err)
        }
    }()

    a := 10
    b := 0
    _ = a / b
    fmt.Println("return")
}

在这个示例中,我们通过a/b,因为b 是 0 所以会导致程序 panic。但是,我们在 defer 语句中使用了 recover,当程序 panic 时,会执行 defer 语句中的匿名函数。这个匿名函数调用 recover 函数,如果有错误信息,则输出错误信息,并恢复程序的正常执行。

需要注意的是,recover 函数只能在 defer 函数中使用,否则会引发运行时错误。此外,recover 函数只会在发生 panic 时返回错误信息,如果没有 panic,则会返回 nil

思考题

  1. 计算 100000 以内偶数,并且不是 4 的倍数外的所有数值和
  2. 定义函数Calculation通过Switch实现加减乘除
// 参考
func Calculation(option byte, a float64,b float64) float64{
    switch option {
    case '-':
    .......    
    }
}
  1. 通过For循环打印如下图形
*
**
***
****
*****
******

参考

gfw.go101.org/article/control-flow...


项目地址

github.com/Zhouchaowen/golang-tuto...

交流群

微信关注【面试情报局】我们一起干翻面试官, 回复golang加入交流群

go
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!