Skip to content

条件语句

条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句。

下图展示了程序语言中条件语句的结构:

Go 语言提供了以下几种条件判断语句:

语句描述
if 语句if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
if...else 语句if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。
if 嵌套语句你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。
switch 语句switch 语句用于基于不同条件执行不同动作。
select 语句select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。

> 注意:Go 没有三目运算符,所以不支持 ?: 形式的条件判断。

if语句

1. 基本 if 语句

最简单的 if 语句形式就是只有条件和代码块。

go
package main

import "fmt"

func main() {
    age := 20

    if age >= 18 {
        fmt.Println("成年人")
    }
}

2. 带 elseif 语句

if 语句可以带一个 else 分支来处理不满足条件的情况。

go
package main

import "fmt"

func main() {
    age := 15

    if age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年")
    }
}

3. 带 else ifelseif 语句

Go 的 if 语句也支持多个条件分支,可以用 else if 来检查其他条件。

go
package main

import "fmt"

func main() {
    score := 85

    if score >= 90 {
        fmt.Println("优秀")
    } else if score >= 75 {
        fmt.Println("良好")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
}

4. 带初始化语句的 if

Go 的 if 语句支持在条件判断之前执行一个简单的初始化语句。这个初始化语句只能在 if 语句内使用,主要用于变量声明或赋值。

go
package main

import "fmt"

func main() {
    if age := 20; age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年")
    }
    // fmt.Println(age) // 这里会报错,因为 age 的作用域仅限于 if 语句内部
}

在这个例子中,age 是在 if 条件判断之前声明的,并且仅在 ifelse 的代码块中可用。

5. 嵌套 if 语句

Go 中也可以嵌套 if 语句,但一般不推荐过多嵌套,会降低代码可读性。

go
package main

import "fmt"

func main() {
    age := 20
    gender := "male"

    if age >= 18 {
        if gender == "male" {
            fmt.Println("成年男性")
        } else {
            fmt.Println("成年女性")
        }
    } else {
        fmt.Println("未成年")
    }
}

Go 中的 if 语句特点

  • 不需要括号:在 Go 中,if 条件不需要用 () 包裹,这一点和 C、C++ 等语言不同。
  • 代码块必须用大括号:即使 if 后面只有一行代码,代码块也必须用 {} 包裹。

总结

  • if 语句用于条件判断,可以带 else ifelse 分支。
  • if 语句可以包含初始化语句,用于在条件判断前初始化变量。
  • Go 的 if 语句不需要条件括号,但代码块必须使用大括号。

swich语句

在 Go 语言中,switch 语句是一种多分支选择结构,通常用于代替一系列的 if-else ifswitch 语句的使用方式灵活且功能强大,支持多种模式的匹配,下面是一些常见的使用方式。

1. 基本 switch 语句

最基本的 switch 语句用于匹配一个表达式的结果。

go
package main

import "fmt"

func main() {
    day := 3

    switch day {
    case 1:
        fmt.Println("星期一")
    case 2:
        fmt.Println("星期二")
    case 3:
        fmt.Println("星期三")
    case 4:
        fmt.Println("星期四")
    case 5:
        fmt.Println("星期五")
    default:
        fmt.Println("周末")
    }
}

在这个例子中,day 的值为 3,所以会输出 "星期三"。

2. 不带表达式的 switch

在 Go 中,switch 可以不带表达式,这种情况下,相当于 switch true。可以用这种方式进行条件判断,类似于 if-else if 结构。

go
package main

import "fmt"

func main() {
    age := 20

    switch {
    case age < 18:
        fmt.Println("未成年")
    case age >= 18 && age < 60:
        fmt.Println("成年人")
    default:
        fmt.Println("老年人")
    }
}

这里的 switch 语句会根据 case 条件判断匹配合适的分支。

3. 多个值的 case

在 Go 的 switch 语句中,每个 case 分支可以包含多个值,使用逗号分隔。只要匹配到其中一个值,就会执行对应的分支。

go
package main

import "fmt"

func main() {
    day := "Saturday"

    switch day {
    case "Saturday", "Sunday":
        fmt.Println("周末")
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("工作日")
    }
}

在这个例子中,如果 day 的值是 "Saturday""Sunday",都会输出 "周末"。

4. fallthrough 关键字

在 Go 中,==每个 case 分支默认不自动向下执行下一个 case==(不同于 C 或 Java 的 switch 语句)。如果想要继续执行下一个 case,可以使用 fallthrough 关键字。

go
package main

import "fmt"

func main() {
    grade := "B"

    switch grade {
    case "A":
        fmt.Println("优秀")
    case "B":
        fmt.Println("良好")
        fallthrough
    case "C":
        fmt.Println("及格")
    default:
        fmt.Println("成绩未知")
    }
}

在这个例子中,grade"B" 时,先输出 "良好",然后因为 fallthrough 会继续执行下一个 case,所以还会输出 "及格"。

5. 带初始化语句的 switch

switch 语句可以包含一个初始化语句,在表达式之前执行。初始化语句的变量只能在 switch 语句内部使用。

go
package main

import "fmt"

func main() {
    switch age := 20; {
    case age < 18:
        fmt.Println("未成年")
    case age >= 18 && age < 60:
        fmt.Println("成年人")
    default:
        fmt.Println("老年人")
    }
}

在这个例子中,age 的初始化只在 switch 语句中有效。

6. switch 匹配类型

switch 还可以用于类型判断,这在处理接口类型时非常有用。

go
package main

import "fmt"

func main() {
    var x interface{}
    x = 10

    switch v := x.(type) {
    case int:
        fmt.Printf("x 是 int 类型,值为 %d\n", v)
    case string:
        fmt.Printf("x 是 string 类型,值为 %s\n", v)
    default:
        fmt.Println("未知类型")
    }
}

在这个例子中,xinterface{} 类型,通过 switch v := x.(type) 可以判断 x 的具体类型。

总结

  • switch 语句可以用于多分支条件判断,比 if-else if 更简洁。
  • switch 可以不带表达式,进行布尔条件判断。
  • case 分支可以包含多个值,用逗号分隔。
  • fallthrough 关键字用于继续执行下一个 case
  • switch 可以带初始化语句。
  • switch 支持类型匹配,用于判断接口的动态类型。

select语句

在 Go 语言中,select 语句是专门用于处理通道(channel)的多路复用操作。它允许 Goroutine 等待多个通道操作,直到其中一个通道可以执行为止。这在并发编程中非常有用。

select 语句的基本语法

select 的语法和 switch 类似,但它的每一个 case 都必须是一个通道操作。select 会等待其中一个通道可以执行,然后执行相应的代码块。

go
select {
case 操作1:
    // 如果操作1成功执行,执行此代码
case 操作2:
    // 如果操作2成功执行,执行此代码
default:
    // 如果没有通道可以操作,执行此代码(可选)
}

示例用法

1. 基本的 select 示例

以下示例展示了如何使用 select 来选择可以操作的通道。

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    // 启动一个 Goroutine,在 2 秒后向 ch1 发送数据
    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "从 ch1 收到数据"
    }()

    // 启动另一个 Goroutine,在 1 秒后向 ch2 发送数据
    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "从 ch2 收到数据"
    }()

    // 使用 select 等待其中一个通道可以执行
    select {
    case msg := <-ch1:
        fmt.Println(msg)
    case msg := <-ch2:
        fmt.Println(msg)
    }
}

在这个例子中,select 等待 ch1ch2 中的一个通道接收到数据。因为 ch2 会在 1 秒后接收到数据,而 ch1 是在 2 秒后,所以会优先输出 "从 ch2 收到数据"

2. 使用 default 分支

select 语句还可以包含一个 default 分支。如果所有通道都阻塞了,则会立即执行 default 分支。

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)

    select {
    case msg := <-ch:
        fmt.Println(msg)
    default:
        fmt.Println("没有通道可以操作,执行默认情况")
    }
}

在这个例子中,因为 ch 没有数据可接收,select 会立即执行 default 分支,输出 "没有通道可以操作,执行默认情况"

3. 使用 select 实现超时

在并发编程中,超时是常见的需求。可以通过 selecttime.After 实现超时处理。

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "操作完成"
    }()

    select {
    case msg := <-ch:
        fmt.Println(msg)
    case <-time.After(1 * time.Second):
        fmt.Println("操作超时")
    }
}

在这个例子中,ch 中的数据会在 2 秒后发送,而 select 中设置了 1 秒的超时。因为超时时间到了还没有数据,程序会输出 "操作超时"

4. 使用 for 循环和 select 处理多个通道

通过 for 循环和 select 的组合,可以不断地监听多个通道。

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        for {
            time.Sleep(1 * time.Second)
            ch1 <- "ch1 数据"
        }
    }()

    go func() {
        for {
            time.Sleep(2 * time.Second)
            ch2 <- "ch2 数据"
        }
    }()

    for i := 0; i < 5; i++ { // 监听5次
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

这个程序会同时监听 ch1ch2,每次通道有数据可读取时就会打印输出。

总结

  • select 语句可以在多个通道上进行等待,适合在并发编程中处理多路通道选择。
  • selectdefault 分支在所有通道都阻塞时执行。
  • 可以结合 time.After 实现超时机制。
  • selectfor 结合可以实现长时间监听多个通道。

Go 中的 select 提供了强大的并发控制能力,尤其适合处理通道的异步操作。

循环语句

在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。

以下为大多编程语言循环程序的流程图:

Go 语言提供了以下几种类型循环处理语句:

循环类型描述
for 循环重复执行语句块
循环嵌套在 for 循环中嵌套一个或多个 for 循环

for语句

在 Go 语言中,for 语句是唯一的循环结构。Go 没有 whiledo-while 循环,但可以通过 for 实现所有类型的循环需求。以下是 Go 中 for 语句的各种用法。

1. 基本的 for 循环

基本的 for 循环结构与其他语言的 for 循环相似,包含初始化语句、条件表达式和递增(或递减)语句。

go
package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}

在这个例子中,i0 开始,每次循环增加 1,当 i < 5 时继续循环。输出结果为:

0
1
2
3
4

2. 省略初始化语句和递增语句

在 Go 中,for 循环可以省略初始化语句或递增语句,甚至条件表达式,这样就可以实现类似 while 的循环。

go
package main

import "fmt"

func main() {
    i := 0
    for i < 5 {
        fmt.Println(i)
        i++
    }
}

在这个例子中,只保留了条件表达式 i < 5,省略了初始化和递增语句,效果等同于 while 循环。

3. 无限循环

如果省略条件表达式for 将变成无限循环。这相当于其他语言的 while(true) 循环。

go
package main

import "fmt"

func main() {
    i := 0
    for {
        fmt.Println(i)
        i++
        if i == 5 {
            break // 用 break 退出循环
        }
    }
}

在这个例子中,循环本身是无限的,但通过 if 条件和 break 语句在 i == 5 时退出循环。

4. for-range 循环

for-range 循环用于遍历数组、切片、映射、字符串和通道,特别方便。

遍历数组或切片

go
package main

import "fmt"

func main() {
    arr := []int{10, 20, 30, 40, 50}

    for index, value := range arr {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
}

输出:

yaml
Index: 0, Value: 10
Index: 1, Value: 20
Index: 2, Value: 30
Index: 3, Value: 40
Index: 4, Value: 50

在这里,range 返回两个值:索引 index 和当前元素的值 value

遍历映射(map

go
package main

import "fmt"

func main() {
    m := map[string]int{"Alice": 25, "Bob": 30}

    for key, value := range m {
        fmt.Printf("Key: %s, Value: %d\n", key, value)
    }
}

输出的顺序可能不同,因为 map 是无序的。

遍历字符串

在遍历字符串时,range 会返回字符的字节索引和 Unicode 码点(rune 类型)。

go
package main

import "fmt"

func main() {
    str := "Hello, 世界"

    for index, char := range str {
        fmt.Printf("Index: %d, Character: %c\n", index, char)
    }
}

5. 嵌套 for 循环

Go 支持嵌套的 for 循环,通常用于多维数据结构。

go
package main

import "fmt"

func main() {
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("i=%d, j=%d\n", i, j)
        }
    }
}

总结

  • Go 只有 for 一种循环语句,可以用它实现所有循环结构。
  • for 支持带条件、带初始化的循环,也可以省略条件变成无限循环。
  • for range 用于遍历数组、切片、映射、字符串和通道。
  • 可以使用 breakcontinue 控制循环流,break 还可以搭配标签退出多层嵌套循环。

range语句

Go语言中,range 循环是一种用于遍历数据结构的语法,常用于数组、切片、映射、字符串和通道的遍历。range 提供了一种简单而简洁的方式,可以在循环中获取元素的索引和值,非常适合遍历各种集合类型的数据。

基本语法

range 的基本语法如下:

go
for index, value := range collection {
    // 使用 index 和 value
}
  • index 是集合元素的索引(如果是字符串则是字符的字节位置,如果是映射则是键)。
  • value 是集合元素的值。

你可以根据需要忽略 indexvalue

示例:遍历数组和切片

go
package main

import "fmt"

func main() {
    arr := []int{10, 20, 30, 40, 50}

    // 使用 range 遍历数组
    for index, value := range arr {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
}

输出:

yaml
Index: 0, Value: 10
Index: 1, Value: 20
Index: 2, Value: 30
Index: 3, Value: 40
Index: 4, Value: 50

忽略 indexvalue

在一些场景下,只需要索引或者只需要值,可以使用 _ 忽略不需要的部分:

只获取值

go
for _, value := range arr {
    fmt.Println("Value:", value)
}

只获取索引

go
for index := range arr {
    fmt.Println("Index:", index)
}

遍历字符串

range 可以用于字符串的遍历,此时 index 是字符的字节位置,value 是字符的 Unicode 值。

go
str := "Hello, 世界"

for index, char := range str {
    fmt.Printf("Index: %d, Char: %c\n", index, char)
}

输出:

yaml
Index: 0, Char: H
Index: 1, Char: e
Index: 2, Char: l
Index: 3, Char: l
Index: 4, Char: o
Index: 5, Char: ,
Index: 6, Char:  
Index: 7, Char: 
Index: 10, Char: 

这里需要注意的是,中文字符在UTF-8编码下占用多个字节,因此字符的索引可能不连续。

遍历映射(Map)

在遍历映射时,range 会返回键和值。值得注意的是,Go语言中的映射是无序的,所以遍历顺序可能是随机的。

go
m := map[string]int{"a": 1, "b": 2, "c": 3}

for key, value := range m {
    fmt.Printf("Key: %s, Value: %d\n", key, value)
}

输出示例:

yaml
Key: a, Value: 1
Key: b, Value: 2
Key: c, Value: 3

遍历映射时,顺序每次可能不同。

遍历通道(Channel)

range 可以用于通道的遍历,当通道被关闭后,range 会自动退出。

go
package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch) // 关闭通道

    for value := range ch {
        fmt.Println("Received:", value)
    }
}

输出:

Received: 1
Received: 2
Received: 3

注意事项

  1. 遍历数组、切片和字符串时index 是整数索引,value 是当前索引处的值。
  2. 遍历映射时index 是键,value 是键对应的值。
  3. 遍历通道时range 会阻塞直到通道关闭,否则会无限阻塞在通道上。

总结

range 循环是Go语言中高效、灵活的循环结构,适用于多种数据结构的遍历。灵活运用 range 可以让代码更简洁。

循环控制语句

循环控制语句可以控制循环体内语句的执行过程。

GO 语言支持以下几种循环控制语句:

控制语句描述
break 语句经常用于中断当前 for 循环或跳出 switch 语句
continue 语句跳过当前循环的剩余语句,然后继续进行下一轮循环。
goto 语句将控制转移到被标记的语句。
在 Go 语言中,循环控制语句主要包括 breakcontinuegoto,这些语句用于控制循环的执行流程。这些控制语句可以让我们在特定条件下跳出循环、跳过当前循环或跳转到指定位置。

1. break 语句

break 语句用于提前退出循环,通常在满足特定条件时使用。它可以用于 for 循环,并可以通过标签退出多层嵌套循环。

示例:使用 break 退出循环

go
package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        if i == 5 {
            break // 在 i 等于 5 时退出循环
        }
        fmt.Println(i)
    }
}

输出0 1 2 3 4

标签的 break:退出嵌套循环

通过为外层循环设置标签,可以在内层循环中使用 break 退出外层循环。

go
package main

import "fmt"

func main() {
outerLoop:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 && j == 1 {
                break outerLoop // 退出外层循环
            }
            fmt.Printf("i=%d, j=%d\n", i, j)
        }
    }
}

输出i=0, j=0 i=0, j=1 i=0, j=2 i=1, j=0

2. continue 语句

continue 语句用于跳过当前循环的剩余部分,直接进入下一次循环。当满足某个条件时,我们可以使用 continue 语句来跳过当前迭代。

示例:使用 continue 跳过某个迭代

go
package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue // 跳过偶数
        }
        fmt.Println(i)
    }
}

输出1 3 5 7 9

在这个例子中,continue 语句跳过了 i 为偶数的情况,因此只打印了奇数。

3. goto 语句

goto 语句用于跳转到程序中的指定标签位置,虽然不建议频繁使用,但在某些场景中,goto 可以简化代码逻辑。标签是一个标识符,后面跟随一个冒号。

示例:使用 goto 跳转

go
package main

import "fmt"

func main() {
    i := 0

loop:
    fmt.Println(i)
    i++
    if i < 5 {
        goto loop // 跳转到标签 loop
    }
}

输出0 1 2 3 4

4. gotobreak 的区别

  • break 只能用于循环语句中,而 goto 可以跳转到代码中的任何标签位置。
  • goto 可以让代码在更灵活的位置继续执行,但容易导致代码难以阅读和理解。
  • 在大部分场景中,breakcontinue 更适合控制循环,而 goto 应尽量避免使用。

总结

  • break:用于退出循环,尤其是在满足某个条件时退出循环。
  • continue:用于跳过本次循环的剩余部分,直接进入下一次循环。
  • goto:直接跳转到指定标签位置,灵活但需谨慎使用,容易导致代码难以维护。

Go 语言的这些控制语句使得循环控制更加灵活,适合各种编程需求。


评论区

欢迎留言、补充或勘误。

xiaoba.blog