Go语言学习笔记(三)------控制结构

Go 提供了下面这些条件结构和分支结构:if-else 结构、switch 结构、select 结构,用于 channel 的选择;可以使用迭代或循环结构来重复执行一次或多次某段代码(任务):for (range) 结构;一些如 break 和 continue 这样的关键字可以用于中途改变循环的状态;此外,使用 return 结束某个函数的执行,或使用 goto 和标签来调整程序的执行位置,Go 完全省略了 if 、 switch 和 for 结构中条件语句两侧的括号。

一、if-else 结构

1.if 是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行 if 后由大括号括起来的代码块,否则就忽略该代码块继续执行后续的代码。如果存在第二个分支,则可以在上面代码的基础上添加 else 关键字以及另一代码块,这个代码块中的代码只有在条件不满足时才会执行。if 和 else 后的两个代码块是相互独立的分支,只可能执行其中一个,如果存在第三个分支,则可以使用下面这种三个独立分支的形式,else-if 分支的数量是没有限制的,但是为了代码的可读性,还是不要在 if 后面加入太多的 else-if 结构:

if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}

2.即使当代码块之间只有一条语句时,大括号也不可被省略。关键字 if 和 else 之后的左大括号 { 必须和关键字在同一行,如果你使用了 else-if 结构,则前段代码块的右大括号 }必须和 else-if 关键字在同一行。这两条规则都是被编译器强制规定的。

//非法的 Go 代码
if x{
}
else { // 无效的
}

3.当 if 结构内有 break、continue、goto 或者 return 语句时,Go 代码的常见写法是省略 else 部分(另见第 5.2 节)。无论满足哪个条件都会返回 x 或者 y 时,一般使用以下写法:

if condition {
return x
}
return y

4.有用的例子:判断一个字符串是否为空:if str == "" { ... } 、if len(str) == 0 {...};判断运行 Go 程序的操作系统类型,这可以通过常量 runtime.GOOS 来判断,这段代码一般被放在 init() 函数中执行。示例:

if runtime.GOOS == "windows" {
. ..
} else { // Unix-like
. ..
}

来演示如何根据操作系统来决定输入结束的提示:

var prompt = "Enter a digit, e.g. 3 "+ "or %s to quit."
func init() {
if runtime.GOOS == "windows" {
prompt = fmt.Sprintf(prompt, "Ctrl+Z, Enter")
} else { //Unix-like
prompt = fmt.Sprintf(prompt, "Ctrl+D")
}
}

isGreater 用于比较两个整型数字的大小:

func isGreater(x, y int) bool {
if x > y {
return true
}
return false
}

if 可以包含一个初始化语句(如:给一个变量赋值)。这种写法具有固定的格式(在初始化语句后方必须加上分号):

if initialization; condition {
// do something
}

示例:

if val := 10; val > max { //使用简短方式 := 声明的变量的作用域只存在于 if 结构中
// do something
}

5.示例:

package main
import "fmt"
func main() {
var first int = 10
var cond int
if first <= 0 {
fmt.Printf("first is less than or equal to 0\n")
} else if first > 0 && first < 5 {
fmt.Printf("first is between 0 and 5\n")
} else {
fmt.Printf("first is 5 or greater\n")
}
if cond = 5; cond > 10 {
fmt.Printf("cond is greater than 10\n")
} else {
fmt.Printf("cond is not greater than 10\n")
}
}

二、 测试多返回值函数的错误

1.使用一个 error 类型的变量来代替作为第二个返回值:成功执行的话,error 的值为 nil,否则就会包含相应的错误信息(Go 语言中的错误类型为 error: var err error )。使用一个 if 语句来测试执行结果,由于其符号的原因,这样形式又称之为 comma,ok 模式(pattern)。

2.测试 err 变量是否包含一个真正的错误( if err != nil )的用法。如果确实存在错误,则会打印相应的错误信息然后通过 return 提前结束函数的执行。我们还可以使用携带返回值的 return 形式,例如 return err 。

//习惯用法
value, err := pack1.Function1(param1)
if err != nil {
fmt.Printf("An error occured in pack1.Function1 with parameter %v", param1)
return err
}
// 未发生错误,继续执行:

函数调用者属于 main 函数,所以程序会直接停止运行。如果其他函数想要在错误发生的同时终止程序的运行,使用 os 包的 Exit 函数:

//习惯用法
if err != nil {
fmt.Printf("Program stopping with error %v", err)
os.Exit(1) //退出代码 1 可以使用外部脚本获取到。
}

3.示例 :可以将错误的获取放置在 if 语句的初始化部

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

4.示例 :或者将 ok-pattern 的获取放置在 if 语句的初始化部分,然后进行判断:

if value, ok := readData(); ok { //如果没有错误ok,执行一下程序。
…
}

您将字符串转换为整数时,且确定转换一定能够成功时,可以将 Atoi 函数进行一层忽略错误的封装:

func atoi (s string) (n int) {
n, _ = strconv.Atoi(s)
return
}

三、switch 结构

1.Go 语言中的 switch 结构接受任意形式的表达式,变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。前花括号 { 必须和 switch 关键字在同一行。可以同时测试多个可能符合条件的值,使用逗号分割它们,例如: case val1, val2, val3 。

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

2.,程序也不会自动地去执行下一个分支的代码。如果在执行完每个分支的代码后,还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的。

switch i {
case 0: fallthrough
case 1:
f() // 当 i == 0 时函数也会被调用
}

3.示例一:

package main
import "fmt"
switch 结构
95
func main() {
var num1 int = 100
switch num1 {
case 98, 99:
fmt.Println("It's equal to 98")
case 100:
fmt.Println("It's equal to 100")
default:
fmt.Println("It's not equal to 98 or 100")
}
}

4.示例二:任何支持进行相等判断的类型都可以作为测试表达式的条件,包括 int、string、指针等。

package main
import "fmt"
func main() {
var num1 int = 7
switch {
case num1 < 0:
fmt.Println("Number is negative")
case num1 > 0 && num1 < 10:
fmt.Println("Number is between 0 and 10")
default:
fmt.Println("Number is 10 or greater")
}
}

5.示例三:switch 语句包含一个初始化语句:

switch result := calculate(); {
case result < 0:
...
case result > 0:
...
default:
// 0
}

四、for 结构

1.重复执行某些语句,Go 语言只有 for 结构可以使用。最简单基于计数器的迭代,基本形式:for 初始化语句; 条件语句; 修饰语句 {}。

package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Printf("This is the %d iteration\n", i)
}
}

2. 基于条件判断的迭代:for 结构的第二种形式是没有头部的条件判断迭代(类似其它语言中的 while 循环),基本形式为: for 条件语句 {} 。您也可以认为这是没有初始化语句和修饰语句的 for 结构,因此 ;; 便是多余的了。

package main
import "fmt"
func main() {
var i int = 5
for i >= 0 {
i = i - 1
fmt.Printf("The variable i is now: %d\n", i)
}
}

3.无限循环: for { } ,如果 for 循环的头部没有条件语句,那么就会认为条件永远为 true,因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。想要直接退出循环体,可以使用 break 语句或 return 语句直接返回。但这两者之间有所区别,break 只是退出当前的循环体,而 return 语句提前对函数进行返回,不会执行后续的代码。无限循环的经典应用是服务器,用于不断等待和接受新的请求。

for t, err = p.Token(); err == nil; t, err = p.Token() {
...
}

4.for-range 结构是 Go 特有的一种的迭代结构,它可以迭代任何一个集合(包括数组和 map),可以获得每次迭代所对应的索引。一般形式为: for ix, val := range coll { } 。 val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到
集合中原有的值(如果 val 为指针,则会产生指针的拷贝,依旧可以修改集合中的原值)。一个字符串是Unicode 编码的字符(或称之为 rune )集合,因此您也可以用它迭代字符串:

  package main
  import (
  	"fmt"
  )

    str := "Go is a beautiful language!"
	fmt.Printf("The length of str is: %d\n", len(str))
	for pos, char := range str {
		fmt.Printf("Character on position %d is: %c \n", pos, char)
	}
	fmt.Println()
	str2 := "Chinese: 日本語"
	fmt.Printf("The length of str2 is: %d\n", len(str2))
  //每个 rune 字符和索引在 for-range 循环中是一一对应的。它能够自动根据 UTF-8 规则识别 Unicode 编码的字符。
	for pos, char := range str2 {
		fmt.Printf("character %c starts at byte position %d\n", char, pos)
	}
	fmt.Println()
	fmt.Println("index int(rune) rune char bytes")
	for index, rune := range str2 {
		fmt.Printf("%2d %d %U '%c' % X\n", index, rune, rune, rune, []byte(string(rune)))
	}
}

五、Break 与 continue

1.判断是否需要停止循环。如果退出条件满足,则使用 break 语句退出循环。一个 break 的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。

package main
func main() {
for i:=0; i<3; i++ {
for j:=0; j<10; j++ {
if j>5 {
break
}
print(j)
}
print(" ")
}
}

2.关键词continue 忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件,关键字 continue 只能被用于 for 循环中。

package main
func main() {
for i := 0; i < 10; i++ {
if i == 5 {
continue
}
print(i)
print(" ")
}
}

六、标签与 goto

1.for、switch 或 select 语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号( : )结尾的单词,标签的名称是大小写敏感的,为了提升可读性,一般建议使用全部大写字母。

package main
import "fmt"
func main() {
LABEL1:
for i := 0; i <= 5; i++ {
for j := 0; j <= 5; j++ {
if j == 4 {
continue LABEL1 //果将 continue 改为 break,则不会只退出内层循环,而是直接退出外层循环了。
}
fmt.Printf("i is: %d, and j is: %d\n", i, j)
}
}
}

2.使用 goto 语句和标签配合使用来模拟循环。特别注意 使用标签和 goto 语句是不被鼓励的:它们会很快导致非常糟糕的程序设计,而且总有更加可读的替代方案来实现相同的需求。

package main
func main() {
i:=0
HERE:
print(i)
i++
if i==5 {
return
}
goto HERE
}

如果您必须使用 goto,应当只使用正序的标签(标签位于 goto 语句之后),但注意标签和 goto 语句之间不能出现定义新变量的语句,否则会导致编译失败。

package main
import "fmt"
func main() {
a := 1
goto TARGET // compile error
b := 9
TARGET:
b += a
fmt.Printf("a is %v *** b is %v", a, b)
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值