Golang 笔记 3 if、switch、for、select语句

1、if语句

  Go的流程控制主要包括条件分支、循环和并发。
  if语句通常由if关键字、条件表达式和由花括号包裹的代码块组成。在Go中,代码块必须由花括号包裹。这里的条件表达式是结果类型为bool的表达式。例:java

if number > 10 { number += 3 }

  else分支:算法

if number > 10 { } else { }

  if语句还支持串联数组

if number > 100 { } else if number < 100 { } else { }

  上面的number变量能够用单独的语句来声明也能够直接加入到if子句中:并发

if number := 1; number > 100 { }

  这里的number := 1被叫作if语句的初始化子句。它应被放置在if关键字和条件表达式之间,并与前者由空格间隔,与后者由英文分号间隔。咱们在这里使用了短变量声明语句,即:在声明变量number的同时为它赋值。它的做用域仅在这条if语句所表明的代码块中。也能够说,变量number对于该if语句以外的代码来讲是不可见的。
  若是咱们在if语句以外声明了number变量,那么number := 1也是合法的。ui

var number int if number := 1; number > 100 { }

  这种写法有一个专有名词:标识符的重声明。只要对同一个标识符的两次声明各自所在的代码块之间存在包含的关系,就会造成对该标识符的重声明。具体到这里,第一次声明的number变量所在的是该if语句的外层代码块,而number := 1所声明的number变量所在的是该if语句的代码块。它们之间存在包含关系,因此对number的重声明就造成了。
  这种状况形成的结果就是,if语句内部对number的访问和赋值都只会涉及到第二次声明的那个number变量。这种现象叫作标识符的遮蔽。编码

2、switch语句

  switch语句提供了一个多分支条件执行的方法。每个case能够携带一个表达式或一个类型说明符。前者又可被简称为case表达式。所以,Go语言的switch语句又分为表达式switch语句和类型switch语句。spa

一、表达式switch语句

var name string
...
switch name { case "Golang": fmt.Println("Golang") case "Rust": fmt.Println("Rust") default: fmt.Println("PHP是世界上最好的语言") }

  Go会依照从上至下的顺序对每一条case语句中case表达式进行求值,只要被发现其表达式与switch表达式的结果相同,该case语句就会被选中。其他的case语句会被忽略。   与if相同,switch语句还能够包含初始化字句,且其出现位置和写法一模一样:3d

names := []string{"Golang","java","PHP"} switch name:=names[0];name { case "Golang": fmt.Println("Golang") ... default: fmt.Println("Unknown") }

二、类型switch语句

  类型switch语句与通常形式有两点差异。第一点,紧随case关键字的不是表达式,而是类型说明符。类型说明符由若干个类型字面量组成,且多个类型字面量之间由英文逗号分隔。第二点,它的switch表达式是很是特殊的。这种特殊的表达式也起到了类型断言的做用,但其表现形式很特殊,如:v.(type),其中v必须表明一个接口类型的值。该类表达式只能出如今类型switch语句中,且只能充当switch表达式。一个类型switch语句的示例以下:指针

v := 11 switch i := interface{}(v).(type) { case int, int8, int16, int32, int64: fmt.Println("A signed integer:%d. The type is %T. \n", v, i) case uint, uint8, uint16, uint32, uint64: fmt.Println("A unsigned integer: %d. The type is %T. \n", v, i) default: fmt.Println("Unknown!") }

  咱们这里把switch表达式的结果赋给了一个变量。如此以来,咱们就能够在该switch语句中使用这个结果了。这段代码被执行后,输出:"A signed integer:11. The type is int."
  最后说一下fallthrough。它既是一个关键字,又能够表明一条语句。fallthrough语句可被包含在表达式switch语句中的case语句中。它的做用是使控制权流转到下一个case。不过要注意fallthrough语句仅能做为case语句中的最后一条语句出现。而且,包含它的case语句不是其所属switch语句的最后一条case语句。code

3、for

  一条for语句一般由关键词for、初始化字句、条件表达式、后置子句和以花括号包裹的代码块组成。其中,初始化字句、条件表达式和后置子句之间需用分号分隔。例:

for i := 0; i < 10; i++ { fmt.Print(i, " ") }

  初始化子句、条件表达式、后置子句中的任何一个或多个,不过起到分隔做用的分号通常须要被保留下来。除非在仅有条件表达式或三者全被省略时分号才能够被一同省略。
  能够把上述的初始化子句、条件表达式、后置子句合称为for子句。for语句还有另一种编写方式,那就是用range字句替换掉for字句。range子句包含一个或两个迭代变量(用于与迭代出的值绑定)、特殊标记:=或=、关键字range以及range表达式。其中,range表达式的结果值的类型应该是可以被迭代的,包括:字符串类型、数组类型、数组的指针类型、切片类型、字典类型和通道类型。例:

for i, v := range "Go语言" { fmt.Printf("%d: %c\n", i, v) }

  对于字符串类型的被迭代值来讲,for语句每次会迭代出两个值,第一个值表明第二个值在字符串中的索引,而第二个值则表明该字符串中的某一个字符。迭代是以索引递增的顺序进行的。上面的代码输出:

0: G 1: o 2: 5: 

  能够看到迭代出的索引值并非连续的,字符串的底层是以字节数组的形式存储的。而在Go中,字符串到字节数组的转换是经过对其中的每一个字符进行UTF-8编码来完成的。字符串"Go语言"中的每个字符与相应的字节数组之间的对应关系以下:
  

  一个中文字符在通过UTF-8编码以后会表现为三个字节。因此咱们用语[0],语[1]、语[2]分别表示字符'语'经编码后的第1、2、三个字节。
  这就能够解释为何会打印出如上内容了:每次迭代出的第一个值所表明的是第二个字符值经编码后的第一个字节在该字符串经编码后的字节数组中的索引值。
  对于数组值、数组的指针值和切片来讲,range子句每次也会被迭代出两个值。其中,第一个值会是第二个值在迭代值中的索引,而第二个值则是被迭代值中的某一个元素。一样的,迭代是以索引递增的顺序进行的。
  对于字典值来讲,range子句每次仍然会迭代出两个值。显然,第一个值是字典中的某一个键,而第二个值则是该键对应的那个值。注意,对字典值上的迭代,Go语言是不保证其顺序的。
  携带range子句的for语句还能够应用在一个通道值之上。其做用是不断地从该通道值中接受数据,不过每次只会接收一个值。若是通道值中没有数据,那么for语句的执行会处于阻塞状态。不管怎样,这样的循环会一直进行下去。直至该通道值被关闭,for语句的执行才会结束。

4、select语句

  select语句属于条件分支流程控制语句,不过它只能用于通道。它能够包含若干条case语句,并根据条件选择其中之一执行。select语句的case关键词只能后跟用于通道的发送操做的表达式以及接受操做的表达式或语句。例:

ch1 := make(chan int, 1) ch2 := make(chan int, 1) ... select { case e1 := <-ch1: fmt.Printf("1th case is selected. e1=%v.\n", e1) case e2 := <-ch2: fmt.Printf("2th case is selected. e2=%v.\n", e2) default: fmt.Printf("No data!") }

  若是该select语句执行的时候通道ch1\ch2都没有任何数据,那么确定只有default case会被执行。可是,只要有一个通道在当时有数据就不会轮到default case执行了。显然,对于包含通道接收操做的case来说,其执行条件就是通道中存在数据。若是在当时有数据的通道多于一个,那么Go会经过一种伪随机的算法来决定哪个case将被执行。
  另外,对于包含通道发送操做的case来说,其执行条件就是通道中至少还能缓冲一个数据(或者说通道未满)。相似,当有多个case中的通道未满时,Go会随机选择:

ch3 := make(chan int, 100) ... select { case ch3 <- 1: fmt.Printf("Sent %d\n", 1) case ch3 <- 2: fmt.Printf("Send %d\n", 2) default: fmt.Println("Full channel!") }

  该条select语句的两个case中包含的都是针对通道ch3的发送操做。若是咱们把这条语句置于一个循环中,那么就至关于用有限范围的随机整数集合去填满一个通道。  **若是一个select语句中不存在default case,而且被执行时其中的全部case都不知足条件,那么它的执行就会被阻塞!**当前进程的进行也会所以而停滞。直到其中一个case知足了执行条件,执行才会继续。咱们一直在说case 执行条件的知足与否取决于其操做的通道在当时的状态。未被初始化的通道会使操做它的case永远知足不了执行条件。  break语句也能够被包含在select语句中的case语句中。它的做用是当即结束当前的select语句的执行。不论其所属的case语句中是否还有未被执行的语句。

相关文章
相关标签/搜索