• Go基础系列:流程控制结构


    条件判断结构:if else
    分支选择结构:switch case
    循环结构:for
    break:退出for或switch结构(以及select)
    continue:进入下一次for迭代

    虽然Go是类C的语言,但Go在这些流程控制语句中的条件表达式部分不使用括号。甚至有些时候使用括号会报错,但有些复杂的条件判断需要使用括号改变优先级。

    如:

    if (name == "longshuai" && age > 23) || (name == "xiaofang" && age < 22) {
        print("yeyeye!!!")
    }
    

    if语句

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

    注意,Go对语法要求很严格。左大括号{必须和if、else或else if在同一行,右大括号}必须换行,如果有else或else if,则必须紧跟这两个关键字。也就是说,上面的代码结构中,大括号的使用位置是强制规范的,不能随意换行放置。

    在Go中,if语句的condition前面可以加上初始化语句,例如Go中很常见的:

    if val := 10; val > max {
        // do something
    }
    

    它在一定程度上等价于:

    val := 10
    if val > max {
        // do something
    }
    

    但注意,前面简写的方式中,val的作用域只在if范围内,if外面无法访问这个val。如果在if语句之前已经定义了一个val,那么这个val将被if中的val掩盖,直到if退出后才恢复。

    func main() {
    	val := 20
    	if val := 10; val > 3 {
    		println("true")
    	}
    	println(val)    // 输出20
    }
    

    一种解决方式是if中的初始化语句不要使用:=,而是直接使用=,但这样会修改原始的值。

    func main() {
    	val := 20
    	if val = 10; val > 3 {
    		println("true")
    	}
    	println(val)    // 输出10
    }
    

    在Go中,经常使用两个(或多个)返回值的函数,一个返回值作为值,另一个作为布尔类型的判断值,或者作为错误信息。通常会使用if语句去检测多个返回值的函数是否成功。

    但注意,一般有两种判断返回值:一种是ok类型,一种是err类型的错误信息。前者是布尔值,后者是表明错误信息的字符串,如果没错误,则err为nil。

    value,ok := func_name()
    if !ok {
        // func_name执行错误
        os.Exit(1)
    }
    
    value,err := func_name()
    if err != nil {
        // func_name执行错误
        os.Exit(1)
        // 或 return err
    }
    

    将上面的简写一下,得到更常见的判断方式:

    if value,ok := func_name();ok {
        // ok为true,函数执行成功
    } else {
        // ok为false,函数执行失败
        os.Exit(1)
    }
    
    if value,err := func_name();err != nil {
        // err不为nil,说明出现错误
        return err
        //或os.Exit(1)
    } else {
        // err为空,说明执行正确
    }
    

    switch语句

    switch语句用于提供分支测试。有两种swithc结构:expression switch和type switch,本文暂时只介绍expression switch,它用于判断表达式是否为true。

    对于expression switch,也有三种形式:等值比较、表达式比较、初始化表达式。

    等值比较结构:当var1的值为val1时,执行statement1,当var1的值为val2时,执行statement2,都不满足时,执行默认的语句statement。

    switch var1 {
        case val1:
            statement1
        case val2:
            statement2
        default:
            statement
    }
    

    等值比较局限性很大,只能将var1和case中的值比较是否相等。如果想比较不等,或者其它表达式类型,可以使用下面的表达式比较结构。

    表达式比较结构:评估每个case结构中的condition,只要评估为真就执行,然后退出(默认情况下)。

    switch {
        case condition1:
            statement1
        case condition2:
            statement2
        default:
            statement
    }
    

    初始化表达式:可以和if一样为switch加上初始化表达式,同样作用域只在switch可见。但注意,initialization后面记得加上分号";"结尾。见下文示例。

    switch initialization; {  // 不要省略分号
        case condition1:
            statement1
        case condition2:
            statement2
        defautl:
            statement
    }
    

    default是可选的,且可以写在switch的任何位置。

    如果case中有多个要执行的语句,可以加大括号,也可以不加大括号。当只有一个语句的时候,statement可以和case在同一行。

    case中可以提供多个用于测试的值,使用逗号分隔,只要有一个符合,就满足条件:

    switch var1 {
        case val1,val2,val3:
            statement1
        case val4,val5: 
            statement2
        default:
            statement
    }
    

    例如:

    val := 20
    switch val {
    case 10, 11, 15:
    	println(11, 15)
    case 16, 20, 22:      // 命中
    	println(16, 20, 22)
    default:
    	println("nothing")
    }
    

    即使是表达式比较结构,也一样可以使用逗号分隔多个表达式,这时和使用逻辑或"||"是等价的:

    func main() {
    	val := 21
    	switch {
    	case val % 4 == 0:
    		println(0)
    	case val % 4 == 1, val % 4 == 2:  //命中
    		println(1, 2)
    	default:
    		println("3")
    	}
    }
    

    默认情况下case命中就结束,所以所有的case中只有一个会被执行。但如果想要执行多个,可以在执行完的某个case的最后一个语句上加上fallthrough,它会无条件地直接跳转到下一条case并执行,如果下一条case中还有fallthrough,则相同的逻辑。此外,fallthrough的后面必须只能是下一个case或default,不能是额外的任何语句,否则会报错。

    例如:

    func main() {
    	val := 21
    	switch val % 4 {
    	case 0:
    		println(0)
    	case 1, 2:         // 命中
    		println(1, 2)  // 输出
    		fallthrough    // 执行下一条,无需条件评估
    		// println("sd") //不能加此行语句
    	case 3:
    		println(3)     // 输出
    		fallthrough    // 执行下一条,无需条件评估
    	default:
    		println("end")  // 输出
    	}
    }
    

    执行结果为:

    1 2
    3
    end
    

    fallthrough一般用于跳过某个case。例如:

    swtich i {
        case 0: fallthrough
        case 1: statement1
        default: statement
    }
    

    它表示等于0或等于1的时候都执行statement1。这和前面case中多个评估值的功能是一样的。

    以下是一个初始化表达式结构的switch示例:

    func main() {
    	val := 21
    	switch val := 23; {
    	case val % 4 == 0:
    		println(0,val)
    	case val % 4 == 1 || val % 4 == 2:
    		println(1, 2,val)
    	default:             // 命中
    		println(3,val)   // 输出"3 23"
    	}
    	println(val)         // 输出21
    }
    

    for语句

    Go中只有一种循环结构:for。

    普通格式的for

    // 完整格式的for
    for init; condition; modif { }
    
    // 只有条件判断的for,实现while的功能
    // 要在循环体中加上退出条件,否则无限循环
    for condition { }
    

    例如:

    // 完整格式
    func main() {
        for i := 0; i < 5; i++ {
            fmt.Println(i)
        }
    }
    
    // 只有条件的格式
    func main() {
        var i int = 5
        for i >= 0 {
            i = i - 1
            fmt.Printf(i)
        }
    }
    

    无限循环

    好几种方式实现for的无限循环。只要省略for的条件判断部分就可以实现无限循环。

    for i := 0;;i++ 
    for { } 
    for ;; { }
    for true { }
    

    无限循环时,一般在循环体中加上退出语句,如break、os.Exit、return等。

    for range遍历

    range关键字非常好用,可以用来迭代那些可迭代的对象。比如slice、map、array,还可以迭代字符串,甚至是Unicode的字符串。

    for index,value := range XXX {}
    

    但千万注意,value是从XXX中拷贝的副本,所以通过value去修改XXX中的值是无效的,在循环体中应该总是让value作为一个只读变量。如果想要修改XXX中的值,应该通过index索引到源值去修改(不同类型修改的方式不一样)。

    以迭代字符串为例。

    func main() {
    	var a = "Xiaofang,你好"
    	for index,value := range a {
    		println(index,string(value))
    	}
    }
    

    输出结果:

    0 X
    1 i
    2 a
    3 o
    4 f
    5 a
    6 n
    7 g
    8 ,
    9 你
    12 好
    

    可见,在迭代字符串的时候,是按照字符而非字节进行索引的。

    下面通过value去修改slice将无效。

    func main() {
    	s1 := []int{11,22,33}
    	for index,value := range s1 {
    		value += 1      // 只在for结构中有效
    		fmt.Println(index,value)
    	}
    	fmt.Println(s1)   // for外面的结果仍然是[11 22 33]
    }
    

    要在循环结构中修改slice,应该通过index索引的方式:

    func main() {
    	s1 := []int{11,22,33}
    	for index,value := range s1 {
    		value += 1
    		s1[index] = value
    		fmt.Println(index,value)
    	}
    	fmt.Println(s1)   // [12 23 34]
    }
    

    break和continue

    breake用于退出当前整个循环。如果是嵌套的循环,则退出它所在的那一层循环。break除了可以用在for循环中,还可以用在switch结构或select结构。

    continue用于退出当前迭代,进入下一轮迭代。continue只能用于for循环中。

    标签和goto

    当某一行中第一个单词后面跟一个冒号的时候,Go就认为这是一个标签。例如:

    func main() {
    LABEL1:
    	for i := 0; i <= 5; i++ {
    		for j := 0; j <= 5; j++ {
    			if j == 4 {
    				continue LABEL1
    			}
    			fmt.Printf("i is: %d, and j is: %d
    ", i, j)
    		}
    	}
    }
    

    使用标签能让break、continue以及goto跳转到指定的位置继续往下执行。例如这里的continue LABEL1,当j == 4的时候,就直接跳到外层循环进入下一轮迭代。而break LABEL则指定直接退出LABEL所在的那一层循环。

    goto懒得介绍了,反正没人用,也强烈不建议使用,甚至标签都建议不要使用。一般能使用LABEL或goto的结构,都能改写成其它更好的语句。

    空语句块

    Go中支持空block{},这个大括号有自己的作用域,里面的代码只执行一次,退出大括号就退出作用域。

    func main() {
    	{
    		v := 1
    		{
    			v := 2
    			fmt.Println(v)   // 输出2
    		}
    		fmt.Println(v)   // 输出1
    	}
    }
    
  • 相关阅读:
    不同浏览器对于textarea中换行符的处理
    apache 页面压缩技术mod_deflate安装和配置
    php5+Apache2+mysql+Eclipse debug完全配置
    php程序的国际化(转载)
    PHP加载mysql DDL会失败
    mysql绿色版注册服务
    DEDE利用自由列表创建全站RSS订阅内容更新整站.xml文档
    PHP模拟多线程请求
    在Mac OS X中配置Apache + PHP + MySQL(转)
    ThinkPHP框架项目开发专题自动验证
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/9866091.html
Copyright © 2020-2023  润新知