• 8.Go语言-流程控制


    1.go语言流程控制

    1.if条件语句

    • 流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”。

      Go语言中最常用的流程控制有iffor,而switchgoto主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。

    • if 条件判断基本写法:

      if 表达式1{
          分支1
      }else if 表达式2{
          分支2
      }else{
          分支3
      }
      
    • if条件语句:在布尔表达式为true时,其后紧跟的语句块执行,如果为false则不执行。

      package main
      
      import "fmt"
      
      func main() {
      	//基本写法
      	var score = 65
      	if score >= 90 {
      		fmt.Println("A")
      	} else if score > 75 {
      		fmt.Println("B")
      		// 注意 else if 和 else 左大括号位置。
      	} else {
      		fmt.Println("C")
      	}
      }
      
    • if判断特殊写法

      • if条件判断还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,举个例子:
      func main() {
      	//2.if判断特殊写法
      	//此时score在if代码块中才生效
      	if score := 65; score >= 90 {
      		fmt.Println("A")
      	} else if score > 75 {
      		fmt.Println("B")
      
      	} else {
      		fmt.Println("C")
      	}
      }
      
      • 不支持三元操作(三目运算符) "a > b ? a:b"
    • if语句嵌套

      package main
      
      
      import "fmt"
      
      
      
      func main(){
      	var a int = 10
      	var b int = 20
      	if a == 10 {
      		if b == 20 {
      			fmt.Printf("a=%d,b=%d
      ",a,b)
      		}
      	}
      	fmt.Printf("a 值为:%d
      ",a)
      	fmt.Printf("b 值为:%d
      ",b)
      }
      

    2.for循环语句

    • Go 语言中的所有循环类型均可以使用for关键字来完成。

    • Go语言中for循环有3种形式,只有其中一种使用分号:

      1.for init;condition;post {}
      2.for condition {}
      3.for {}
      
      init : 一般为赋值表达式,给控制变量赋值;
      condition:关系表达式或逻辑表达式,循环控制条件;
      post:一般为赋值表达式,给控制变量增量或减量;
      for语句执行过程如下:
      	1.先对表达式 init 赋初始值;
      	2.判断赋值表达式init是否满足给定condition条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
      
    • for循环基本格式如下:

      for 初始语句;条件表达式;结束语句{
      	循环体语句
      }
      
    • 条件表达式返回true时循环体不停地进行循环,直到条件表达式返回false时自动退出循环。

    • for 循环

    package main
    
    import "fmt"
    
    //for循环
    func main() {
    	for i := 0; i < 10; i++ {
    		fmt.Println(i)
    	}
    }
    
    • 省略初始语句,但是必须保留初始语句的分号

      func main() {
      	var i = 0
      	for ; i < 10; i++ {
      		fmt.Println(i)
      	}
      }
      
      
    • 省略初始语句和结束语句

      func main() {
      	var i = 10
      	for i > 0 {
      		fmt.Println(i)
      		i--
      	}
      }
      
    • 无限循环

      for {
      		fmt.Println("hello shahe")
      	}
      
    • break 跳出for循环

      for i := 0; i < 5; i++ {
      		fmt.Println(i)
      		if i == 3 {
      			break
      		}
      	}
      
    • continue继续下一次循环

      for i := 0; i < 5; i++ {
      		if i == 3 {
      			continue //跳过本次for循环,继续下一次循环
      		}
      		fmt.Println(i)
      
      	}
      //0
      //1
      //2
      //4
      
    • 循环的嵌套

      • for循环中嵌套一个或多个for循环
      for [condition |  ( init; condition; increment ) | Range]
      {
         for [condition |  ( init; condition; increment ) | Range]
         {
            statement(s)
         }
         statement(s)
      }
      
    • 循环嵌套输出2-100素数

      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 是素数
      ",i)
      		}
      	}
      }
      

    3.switch case循环

    • switch语句用于基于不同条件执行不同动作,每个case分支都是唯一,从上直下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。

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

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

    • 使用switch语句可方便地对大量的值进行条件判断。

      package main
      
      import "fmt"
      
      //switch
      func main() {
      	finger := 3
      	switch finger {
      	case 1:
      		fmt.Println("大拇指")
      	case 2:
      		fmt.Println("食指")
      	case 3:
      		fmt.Println("中指")
      	case 4:
      		fmt.Println("无名指")
      	case 5:
      		fmt.Println("小拇指")
      	default:
      		fmt.Println("无效输入")
      	}
      }
      //go语规定每个switch只能有一个default分支。一个分支可以有多个值,多个case值中间使用英文逗号分隔。
      
    • case一次判断多个值

    num := 5
    	switch num {
    	case 1, 3, 5, 7, 9:
    		fmt.Println("奇数")
    	case 2, 4, 6, 8:
    		fmt.Println("偶数")
    }
    
    • case 中使用表达式
      • 分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量
    age := 30
    switch {
        case age > 18:
        	fmt.Println("成年人")
        case age < 18:
        	fmt.Println("未成年人")
        default:
        	fmt.Println("不是人")
    }
    
    • fallthrough语法可以执行满足条件的case的下一个case.是为了兼容C语言中case设计的。

    • Type Switch

      • switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
      • 语法:
      switch x.(type) {
      	case type:
      		statement(s)
      	case type:
      		statement(s)
      	default:
      		statement(s)
      }
      
    • demo1

      func main(){
      	var x interface{}
      	// 带初始化语句
      	switch i := x.(type) {
      	case nil:
      		fmt.Printf("x的类型:%T
      ",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("未知型")
      	}
      }
      
    • demo2

      func main() {
      	var j = 0
      	switch j {
      		case 0:
      		case 1:
      			fmt.Println("1")
      		case 2:
      			fmt.Println("2")
      		default:
      			fmt.Println("def")
      	}
      }
      
      
    • demo3

      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")
          }
      // fallthrough
      // 1
      

    4.条件语句select

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

    • select 是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。 select 随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。

    • Go 编程语言中 select 语句的语法如下:

      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
      ")
      		case c2 <- i2:
      			fmt.Printf("send ",i2," to c2
      ")
      		case i3,ok := (<-c3):
      			if ok {
      				fmt.Printf("received ", i3, " from c3
      ")
      			} else {
      				fmt.Printf("c3 is closed
      ")
      			}
      		default:
      			fmt.Printf("no communication
      ")
      	}
      }
      // no communication
      

      select可以监听channel的数据流动

      select的用法与switch语法非常类似,由select开始的一个新的选择块,每个选择条件由case语句来描述

      与switch语句可以选择任何使用相等比较的条件相比,select由比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作

      select { //不停的在这里检测
      case <-chanl : //检测有没有数据可以读
      //如果chanl成功读取到数据,则进行该case处理语句
      case chan2 <- 1 : //检测有没有可以写
      //如果成功向chan2写入数据,则进行该case处理语句
      
      
      //假如没有default,那么在以上两个条件都不成立的情况下,就会在此阻塞//一般default会不写在里面,select中的default子句总是可运行的,因为会很消耗CPU资源
      default:
      //如果以上都没有符合条件,那么则进行default处理流程
      }
      

      在一个select语句中,Go会按顺序从头到尾评估每一个发送和接收的语句。

      如果其中的任意一个语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。 如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况: ①如果给出了default语句,那么就会执行default的流程,同时程序的执行会从select语句后的语句中恢复。 ②如果没有default语句,那么select语句将被阻塞,直到至少有一个case可以进行下去。

    • Go语言中select的使用及典型用法

      • select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。 select中的case语句必须是一个channel操作

        select中的default子句总是可运行的。

        如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。

        如果没有可运行的case语句,且有default语句,那么就会执行default的动作。

        如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行。

      • 超时判定

        //比如在下面的场景中,使用全局resChan来接受response,如果时间超过3S,resChan中还没有数据返回,则第二条case将执行
        var resChan = make(chan int)
        // do request
        func test() {
            select {
            case data := <-resChan:
                doData(data)
            case <-time.After(time.Second * 3):
                fmt.Println("request time out")
            }
        }
        
        func doData(data int) {
            //...
        }
        
      • 退出

        //主线程(协程)中如下:
        var shouldQuit=make(chan struct{})
        fun main(){
            {
                //loop
            }
            //...out of the loop
            select {
                case <-c.shouldQuit:
                    cleanUp()
                    return
                default:
                }
            //...
        }
        
        //再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行
        close(shouldQuit)
        
      • 判断channel是否阻塞

        //在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断
        ch := make (chan int, 5)
        //...
        data:=0
        select {
        case ch <- data:
        default:
            //做相应操作,比如丢弃data。视需求而定
        }
        

    5.循环语句range

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

    • for循环的range格式可以对slice,map,数组,字符串等进行迭代循环,格式如下:

      for key,value := range oldMap{
      	newMap[key] = value
      }
      
    • 课忽略不想要的返回值,或 "_" 这个特殊变量

      package main
      
      func main() {
      	s := "abcde"
      	// 忽略value	
      	for i:=range s{
      		println(s[i])
      	}
      	// 忽略index
      	for _,v := range s{
      		println(v)
      	}
      }
      
      
      package main
      
      func main() {
      	m := map[string]int{"a":1,"b":2}
      	for k,v := range m{
      		println(k,v)
      	}
      }
      
    • range会复制对象

      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]。
      }
      // [0 999 999]
      // [100 101 102]
      
    • 建议改用引用类型,其底层数据不会被复制。slice是引用类型

      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
      

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

    • for 和 for range有什么区别?

      主要是使用场景不同
      for可以
          遍历array和slice
          遍历key为整型递增的map
          遍历string
      for range可以完成所有for可以做的事情,却能做到for不能做的,包括
          遍历key为string类型的map并同时获取key和value
          遍历channel
      

    6.循环控制Goto,Break,Continue

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

      package main
      
      import "fmt"
      
      func main() {
      	for i := 1; i < 10; i++ {
      		for j := 1; j <= i; j++ {
      			fmt.Printf("%v*%v=%v	", i, j, i*j)
      		}
      		fmt.Println("")
      	}
      
      }
      
      
  • 相关阅读:
    从C#到TypeScript
    从C#到TypeScript
    从C#到TypeScript
    从C#到TypeScript
    UWP开源项目 LLQNotifier 页面间通信利器(移植EventBus)
    .net源码分析
    读读日报小布版 计划与反馈
    Pyinstaller使用
    Pyspider安装使用
    元器件资料查看网站
  • 原文地址:https://www.cnblogs.com/xujunkai/p/12878515.html
Copyright © 2020-2023  润新知