Go入门(七)-- 控制结构

流程控制语句一般起以下3个作用:

  • 选择,即根据条件跳转到不同的执行序列
  • 循环,即根据条件反复执行某个序列,当然每一次循环执行的输入输出可能会发生变化
  • 跳转,即根据条件返回到某执行序列

Go 语言支持如下几种流程控制语句:

  • 条件语句:关键字 if、else 和 else if
  • 选择语句:关键字 switch、case 和 select
  • 循环语句:关键字 for 和 range
  • 跳转语句:关键字 goto

为了满足更丰富的控制需求,Go 语言还添加了 break、continue 和 fallthrough 关键字

Go 的控制结构与 C 的控制结构相关,但在重要方面有所不同:

  • 没有 do 或 while 循环,只有一个稍微广义化的 for
  • if 和 switch 接受一个可选的初始化语句,如 for 的初始化语句
  • break 和 continue 语句采用可选标签来标识要中断或继续的内容
  • 出现了包括类型开关和多路通信复用器、select 的新的控制结构
  • 语法略有不同:没有括号,正文必须始终以大括号分隔

条件语句if

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

if 语句

可省略条件表达式括号

持初始化语句,可定义代码块局部变量

代码块左 括号必须在表达式尾部

最简单的 if 语句

var score int = 80
if score >= 60 {
    fmt.Println("继续努力哦!!!")
}

简单写法

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

if...else if...else 语句

var score int = 85
if score >= 80 {
    fmt.Println("很棒哦")
} else if score >= 60 {
    fmt.Println("继续努力哦!!!")
} else {
    fmt.Println("加油!!!")
}

x := 0
if n := "abc"; x > 0 {     // 初始化语句未必就是定义变量, 如 println("init") 也是可以的。
    println(n[2])
} else if x < 0 {    // 注意 else if 和 else 左大括号位置。
    println(n[1])
} else {
    println(n[0])
}

Go 语言不支持三元操作符(三目运算符)

if 嵌套语句

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   /* 判断条件 */
   if a == 100 {
       /* if 条件语句为 true 执行 */
       if b == 200 {
          /* if 条件语句为 true 执行 */
          fmt.Printf("a 的值为 100 , b 的值为 200\n" )
       }
   }
   fmt.Printf("a 值为 : %d\n", a )
   fmt.Printf("b 值为 : %d\n", b )
}

注意:

  1. 条件语句不需要括号将条件包含起来()
  2. 无论语句体内有几条语句,花括号 {} 都是必须存在的
  3. 左花括号 { 必须与 if 或 else 处于同一行
  4. 在 if 之后,条件语句之前,可以添加变量初始化语句,使用 ; 隔开
  5. 在有返回值的函数中,不允许将 "最终的" return 语句包含在 if...else... 结构中,否则会编译失败

选择语句switch 

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上到下逐一执行,知道匹配为止。Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。若需要逻辑处理可使用break。

基本使用

语法

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。

示例代码

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90

   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }

   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )     
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" )
   }
   fmt.Printf("你的等级是 %s\n", grade )
}

Type Switch

switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型(类型断言)。
Type Switch 语法格式如下:

switch x.(type){
    case type:
       statement(s)      
    case type:
       statement(s)
    /* 你可以定义任意个数的case */
    default: /* 可选 */
       statement(s)
}

示例代码

package main

import "fmt"

func main() {
    var x interface{}
    //写法一:
    switch i := x.(type) { // 带初始化语句
    case nil:
        fmt.Printf(" x 的类型 :%T\r\n", i)
    case int:
        fmt.Printf("x 是 int 型")
    case float64:
        fmt.Printf("x 是 float64 型")
    case func(int) float64:
        fmt.Printf("x 是 func(int) 型")
    case bool, string:
        fmt.Printf("x 是 bool 或 string 型")
    default:
        fmt.Printf("未知型")
    }
    //写法二
    var j = 0
    switch j {
    case 0:
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //写法三
    var k = 0
    switch k {
    case 0:
        println("fallthrough")
        fallthrough
        /*
            Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
            而如果switch没有表达式,它会匹配true。
            Go里面switch默认相当于每个case最后带有break,
            匹配成功后不会自动向下执行其他case,而是跳出整个switch,
            但是可以使用fallthrough强制执行后面的case代码。
        */
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //写法三
    var m = 0
    switch m {
    case 0, 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("def")
    }
    //写法四
    var n = 0
    switch { //省略条件表达式,可当 if...else if...else
    case n > 0 && n < 10:
        fmt.Println("i > 0 and i < 10")
    case n > 10 && n < 20:
        fmt.Println("i > 10 and i < 20")
    default:
        fmt.Println("def")
    }
}


/**
输出结果:
x 的类型 :<nil>
fallthrough
1
1
def
*/

注意:

  1. 左花括号 { 必须与 switch 处于同一行
  2. 条件表达式不限制为常量或者整数
  3. 单个 case 中,可以出现多个结果选项
  4. 不需要用 break 来明确推出一个 case
  5. 只有在 case 中明确添加 fallthrough 关键字,才会继续执行紧跟的一个 case
  6. 可以不设定 switch 之后的条件表达式,在此种情况下,整个 switch 结构与多个 if...else... 的逻辑作用等同

选择语句select

select 语句类似于 switch 语句,但是 select 会随机执行一个可运行的 case。如果没有 case 可运行,他将阻塞,直到有 case 可运行。

select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。

select 随机执行一个可运行的 case。如果 case 可运行,它将阻塞,知道 case 可运行。一个默认的字句应该总是可运行的。

语法

select {
    case communication clause  :
       statement(s);      
    case communication clause  :
       statement(s);
    /* 你可以定义任意数量的 case */
    default : /* 可选 */
       statement(s);
}

以下描述了 select 语句的语法

每个case都必须是一个通信
所有channel表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通信可以进行,它就执行;其他被忽略。
如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
否则:
如果有default子句,则执行该语句。
如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
package main

import "fmt"

func main() {
   var c1, c2, c3 chan int
   var i1, i2 int
   select {
      case i1 = <-c1:
         fmt.Printf("received ", i1, " from c1\n")
      case c2 <- i2:
         fmt.Printf("sent ", i2, " to c2\n")
      case i3, ok := (<-c3):  // same as: i3, ok := <-c3
         if ok {
            fmt.Printf("received ", i3, " from c3\n")
         } else {
            fmt.Printf("c3 is closed\n")
         }
      default:
         fmt.Printf("no communication\n")
   }    
}

以上代码执行结果为:

no communication

循环语句for

for 循环是一个循环控制结构,可以执行指定次数的循环。Go 语言中没有 while 和 do...while 循环,但可以通过 for 循环实现相同的效果。

Go 语言的 for 循环有3种形式 ,只有一种形式有分号

语法

// Like a C for
for init; condition; post {}

// Like a C while
for condition {}

// Like a C for(;;)
for {}

参数描述:

  • init: 一般为赋值表达式,给控制变量赋初值
  • condition: 关系表达式或逻辑表达式,循环控制条件
  • post: 一般为赋值表达式,给控制变量增量或减量

for 语句执行过程:

① 先对表达式 init 赋初值

② 判断赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,

然后执行 post,进入第二次循环,再判别 condition;否则判断condition 的值是否为假,不满足条件,就

终止 for 循环,执行循环体外语句。

示例代码

s := "abc"

for i, n := 0, len(s); i < n; i++ { // 常见的 for 循环,支持初始化语句。
    println(s[i])
}

n := len(s)
for n > 0 {                // 替代 while (n > 0) {}
    println(s[n])        // 替代 for (; n > 0;) {}
    n-- 
}

for {                    // 替代 while (true) {}
    println(s)            // 替代 for (;;) {}
}

不要期望编译器能理解你的想法,在初始化语句中计算出全部结果是个好主意。

package main

func length(s string) int {
    println("call length.")
    return len(s)
}

func main() {
    s := "abcd"

    for i, n := 0, length(s); i < n; i++ {     // 避免多次调用 length 函数。
        println(i, s[i])
    } 
}


/**
输出结果
call length.
0 97
1 98
2 99
3 100
*/

实例:

package main

import "fmt"

func main() {

   var b int = 15
   var a int

   numbers := [6]int{1, 2, 3, 5}

   /* for 循环 */
   for a := 0; a < 10; a++ {
      fmt.Printf("a 的值为: %d\n", a)
   }

   for a < b {
      a++
      fmt.Printf("a 的值为: %d\n", a)
      }

   for i,x:= range numbers {
      fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
   }   
}


/**
输出结果
a 的值为: 0
a 的值为: 1
a 的值为: 2
a 的值为: 3
a 的值为: 4
a 的值为: 5
a 的值为: 6
a 的值为: 7
a 的值为: 8
a 的值为: 9
a 的值为: 1
a 的值为: 2
a 的值为: 3
a 的值为: 4
a 的值为: 5
a 的值为: 6
a 的值为: 7
a 的值为: 8
a 的值为: 9
a 的值为: 10
a 的值为: 11
a 的值为: 12
a 的值为: 13
a 的值为: 14
a 的值为: 15
第 0 位 x 的值 = 1
第 1 位 x 的值 = 2
第 2 位 x 的值 = 3
第 3 位 x 的值 = 5
第 4 位 x 的值 = 0
第 5 位 x 的值 = 0
*/

循环嵌套

在 for 循环中嵌套一个或多个 for 循环

语法

for [condition |  ( init; condition; increment ) | Range]
{
   for [condition |  ( init; condition; increment ) | Range]
   {
      statement(s)
   }
   statement(s)
}

实例

以下实例使用循环嵌套来输出 2 和 100 间的素数

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var i, j int

   for i=2; i < 100; i++ {
      for j=2; j <= (i/j); j++ {
         if(i%j==0) {
            break // 如果发现因子,则不是素数
         }
      }
      if(j > (i/j)) {
         fmt.Printf("%d  是素数\n", i)
      }
   }  
}


/*
输出结果:
    2  是素数
    3  是素数
    5  是素数
    7  是素数
    11  是素数
    13  是素数
    17  是素数
    19  是素数
    23  是素数
    29  是素数
    31  是素数
    37  是素数
    41  是素数
    43  是素数
    47  是素数
    53  是素数
    59  是素数
    61  是素数
    67  是素数
    71  是素数
    73  是素数
    79  是素数
    83  是素数
    89  是素数
    97  是素数
*/

无限循环

如果循环中条件语句永远不为 false 则会进行无限循环,我们可以通过 for 循环语句中只设置一个条件表达式来执行无限循环

package main

import "fmt"

func main() {
    for true  {
        fmt.Printf("这是无限循环。\n");
    }
}

循环语句for...range

Golang range 类似迭代器操作,返回(索引,值)或(键,值)。

for 循环的 range 格式可以对 slice、map、数组、字符串、映射、channel等进行迭代循环

基本使用

语法

for key, value := range oldMap {
    newMap[key] = value
}

// 只需要键或索引
for key := range oldMap {
    delete(oldMap,key)
}

// 只需要值
for _, value := range oldMap {
    ...
}

示例代码

可忽略不想要的返回值,或 _ 这个特殊变量。

package main

func main() {
    s := "abc"
    // 忽略 2nd value,支持 string/array/slice/map。
    for i := range s {
        println(s[i])
    }
    // 忽略 index。
    for _, c := range s {
        println(c)
    }
    // 忽略全部返回值,仅迭代。
    for range s {

    }

    m := map[string]int{"a": 1, "b": 2}
    // 返回 (key, value)。
    for k, v := range m {
        println(k, v)
    }
}


/**
97
98
99
97
98
99
a 1
b 2
*/

数据类型取值方式

数据类型

参数一

参数二

string

index

s[index]

unicode, rune

array/slice

index

s[index]

map

key

m[key]

channel

element

复制对象

注意

range 会复制对象

package main

import "fmt"

func main() {
    a := [3]int{0, 1, 2}

    for i, v := range a { // index、value 都是从复制品中取出。

        if i == 0 { // 在修改前,我们先修改原数组。
            a[1], a[2] = 999, 999
            fmt.Println(a) // 确认修改有效,输出 [0, 999, 999]。
        }

        a[i] = v + 100 // 使用复制品中取出的 value 修改原数组。

    }

    fmt.Println(a) // 输出 [100, 101, 102]。
}

建议改用引用类型,其底层数据不会被复制

package main

func main() {
    s := []int{1, 2, 3, 4, 5}

    for i, v := range s { // 复制 struct slice { pointer, len, cap }。

        if i == 0 {
            s = s[:3]  // 对 slice 的修改,不会影响 range。
            s[2] = 100 // 对底层数据的修改。
        }

        println(i, v)
    }
}


/**
输出结果:
0 1
1 2
2 100
3 4
4 5
*/

另外两种引用类型 map、channel 是指针包装,而不像 slice 是 struct

for 和 for range 的区别

主要是使用场景不同

for 可以

  • 遍历 array 和 slice
  • 遍历 key 为整形递增的 map
  • 遍历 string

for range 可以完成 for 可以做的事情,却能做到 for 不能做的,包括

  • 遍历 key 为 string 类型的 map 并同时获取 key 和 value
  • 遍历 channel

循环控制 goto、break、continue 

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

Go 支持三种循环控制语句:goto、break、continue

1.三个语句都可以配合标签(label)使用
2.标签名区分大小写,定以后若不使用会造成编译错误
3.continue、break配合标签(label)可用于多层循环跳出
4.goto是调整执行位置,与continue、break配合标签(label)的结果并不相同

高级的 break

Go 语言提供了一个更高级的 break,可以选择中断哪一个循环,如下:

for j := 0; j < 5; j++ {
    for i := 0; i < 10; i++ {
        if i > 5 {
            break JLoop
        }
        fmt.Println(i)
	}
}
JLoop:
// ...

跳转语句 goto

在开发过程中,尽量不要使用 goto。但 goto 还是会在一些场合下被证明是合适的

func myFunc() {
    i := 0
    HERE:
    fmt.Println(i)
    i++
    if i < 10 {
        goto HERE
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值