• go——流程控制


    Go在流程控制方面的特点如下:
      没有do和while循环,只有一个更广义的for语句。
      switch语句灵活多变,还可以用于类型判断。
      if语句和switch语句都可以包含一条初始化子语句。
      break语句和continue语句可以后跟一条标签(label)语句,以标识需要终止或继承的代码块。
      defer语句可以使我们更加方便地执行异常捕获和资源回收任务。
      select语句也用于多分支选择,但只与通道配合使用。
      go语句用于异步启动goroutine并执行指定函数。

    1.代码块和作用域

    代码块就是一个由花括号包裹地表达式和语句的序列。当然,代码块中也可以不包含任何内容,即:空代码块。
    除了显式地代码块之外,还有一些隐式地代码块,说明如下:

       所有Go代码形成了一个最大地代码块,即:全域代码块
      每一个代码包中的代码共同组成了一个代码块,即:代码包代码块
      每一个源码文件都是一个代码块,即:源码文件代码块
      每一个if、for、switch和select语句都是一个代码块;
      每一个在switch或select语句中的case分支都是一个代码块。

    在Go中,使用代码块表示词法上的作用域范围,具体规则如下:
      一个预定义标识符的作用域是全域代码块
      表示一个常量、变量、类型或函数(不包括方法),且声明在函数之外的标识符的作用域是当前的代码包代码块;
      被导入的代码包的名称的作用域是当前的源码文件代码块;
      表示方法接收者、方法参数、类型或函数的标识符,如果被声明在函数内部,那么作用域就是包含其声明的那个最内层的代码块。

    此外,我们还可以重新声明已经在外层代码块中声明过的标识符。
    当在内层代码块中使用这个标识符时,它表示的总是那个在该代码块中与它绑定在一起的那个程序实体。
    可以说,此时在外层代码块中声明的那个同名标识符并屏蔽了。例如

    package main
    import (
    	"fmt"
    )
    var v = "1,2,3"  //最外层标识符
    func main() {
    	v := []int{1, 2, 3}  //第二次赋值
    	if v != nil {
    		var v = 123  //第三次赋值
    		fmt.Printf("%v
    ", v)  
    	}
    }
    //结果:123
    

    其中,变量v被声明3次。当判断v是否非nil时,v代表的时那个切片。
    而当v被打印时,它代表的确实那个整数。

    2.if语句

    if语句会根据条件表达式来执行两个分支中的一个。
    如果那个表达式的结果是true,那么if分支会被执行,否则else分支会被执行。

    例如:

    var number int
    //省略
    if 100 < number {
    	number++
    }
    

    又如:

    if 100 < number {
    	number++
    }else {
    	number--
    }
    

    if语句还可以包含一条初始化的子语句,用于初始化局部变量:

    if diff := 100 - number;100 < diff {
    	number++
    }else {
    	number--
    }
    

    此外,它还支持串联:

    if diff := 100 - number;100 < diff {  //先进行赋值操作,在逻辑判断
    	number++
    }else if 200 < diff {
    	number--
    }else {
    	number -= 2
    }
    

    其中条件表达式的求值顺序是自上而下的。只有第一个结果为true的表达式对应的分支会被选中并执行。
    并且,只要上面的表达式的结果为true,其后的表达式就不会被求值。

    3.switch语句

    switch语句也提供了一种多行分支执行的方法。
    它会用一个表达式或类型说明符与每一个case进行比较,并决定执行哪一个分支。

    (1)表达式switch语句

    在表达式switch语句中,switch表达式和所有case携带的表达式(也称为case表达式)都会被求值,并且执行顺序是自左向右、自下而上。
    只有第一个与switch表达式的求值结果相等case表达式分支会被执行。
    如果没有找到匹配的case表达式并且存在default case,那么default case的分支会执行。
    注意,default case最多只有一个。
    另外,switch表达式可以省略,这时true会作为switch表达式的结果。
    示例:

    package main
    import (
    	"fmt"
    )
    var content string
    //省略
    switch content {
    	default:
    		fmt.Println("不知道什么语言")
    	case "python":
    		fmt.Fprintln("一门解释型语言")
    	case "go":
    		fmt.Println("一门编译型语言")
    

    switch语句也可以包含一条子语句来初始化局部变量:

    switch lang := strings.TrimSpace(content); lang {
    	default:
    		fmt.Println("不知道什么语言")
    	case "python":
    		fmt.Fprintln("一门解释型语言")
    	case "go":
    		fmt.Println("一门编译型语言")
    }
    

    可以在switch语句中使用fallthrough,来向下一个case语句转移流程控制权。

    switch lang := strings.TrimSpace(content); lang {
    	case "Ruby":
    		fallthrough
    	case "Python":
    		fmt.Println("一门解释型语言")
    	case "C","Java","Go":
    		fmt.Println("一门编译型语言")
    	default:
    		fmt.Println("什么都不是")
    }

    只要lang的值等于Ruby或python,第2个case语句就会执行。其实可以放在一个case中。
    每个case语句中的case表达式还可以有多个。
    另外,break语句可以用来退出当前的switch语句。它由一个break关键字和一个可选的标签组成。

    (2)类型switch语句

    类型switch语句将对类型进行判定,而不是值。

    var v interface{}
    //省略
    switch v.(type) {
    	case string:
    		fmt.Printf("The string '%s'.
    ",v.(string))
    	case int,uint,int8,uint8:
    		fmt.Printf("The integer is %d.
    ",v)
    	default:
    		fmt.Printf("Unsupported value.(type%T)
    ",v)
    }
    

    类型switch语句的switch表达式会包含一个特殊的类型断言,例如v.(type)。
    它虽然特殊,但是也要遵循类型断言的规则。其次,每个case表达式中包含的都是类型字面量而不是表达式。
    最后fallthrough语句不允许出现在类型switch语句中。

    类型断言switch语句的switch表达式还有一种变形写法。

    switch i := v.(type) {
    	case string:
    		fmt.Printf("The string '%s'.
    ",i)
    	case int,uint,int8,uint8:
    		fmt.Printf("The integer is %d.
    ",i)
    	default:
    		fmt.Printf("Unsupported value.(type%T)
    ",i)
    }
    

    这里的i := v.(type)使经类型转换后的值得以保存。i的类型一定会是v的值的实际类型。

    4.for语句

    for语句用于根据给定的条件重复执行一个代码块。这个条件或由for子句直接给出,或从range子句中获得。
    (1)for子句

    一条for语句中可以携带一条for子句。for子句可以包含初始化子句、条件子句和后置子句。

    var number int
    for i := 0; i < 100; i++ {  //i := 0初始化语句  i++后置语句
    	number++
    }
    
    var j uint = 1
    for;j%5 != 0; j *= 3 {  //省略初始化子句
    	number++
    }
    
    for k := 1; k%5 != 0; {  //省略后置子句
    	k *= 3
    	number++
    }
    

    在for子句的初始化子句和后置子句同时被省略,或者其中的所有部分都省略的情况下,分隔符":"可以省略。

    var m = 1
    for m < 50 {
    	m *= 3
    }
    

    (2)range子句

    一条for语句可以携带一条range语句,这样就可以迭代出一个数组或者切片值中的每个元素、
    一个字符串中的每个字符、或者一个字典值中的每个键-元素对,以及持续地接收一个通道类型值中的元素。
    随着迭代的进行,每一次获取的迭代值(索引、元素、字符或键-元素对)都会赋给相应的迭代变量。

    ints := []int{1, 2, 3, 4, 5}
    for i, d := range ints {
    	fmt.Printf("Index:%d,value:%d
    ", i, d)
    }
    

    在range关键字右边的是range表达式。range表达式一般只会在迭代开始前被求值一次.
    针对range表达式的不同结果,range子句的行为也会不同。

    使用range子句,有3点需要注意:
      a.若对数组、切片或字符串值进行迭代,且:=左边只有一个迭代变量时,一定要小心。
       这时只会得到其中元素的索引,而不是元素本身。
      b.迭代没有任何元素的数组值、为nil的切片值、为nil的字典值或为""的字符串值,
        并不会执行for语句中的代码。for语句在一开始就会直接结束执行,因为这些值的长度都为0.
      c.迭代为nil的通道值会让当前流程永远阻塞在for语句上。

  • 相关阅读:
    Silverlight自定义数据绑定控件应该如何处理IEditableObject和IEditableCollectionView对象
    如何把Crystal Reports报表转换为ActiveReports报表
    Linux内核中的seq操作
    ip_vs实现分析(6)
    ip_vs实现分析(9)
    ipvs学习笔记(二)
    ip_vs实现分析(7)
    ip_vs实现分析(8)
    PHP 开发环境搭建( Eclipse PDT XAMPP XDEBUG)
    ip_vs实现分析(5)
  • 原文地址:https://www.cnblogs.com/yangmingxianshen/p/10089895.html
Copyright © 2020-2023  润新知