美文网首页
4. Go 程序结构控制

4. Go 程序结构控制

作者: PRE_ZHY | 来源:发表于2018-09-18 20:59 被阅读2次

    当编写一个有一丢丢难度的程序的时候,就一定会涉及到程序结构(流向)的控制:需要一步一步顺序执行;需要根据不同情况进行选择,运行不同的代码;对相同的重复的事情,循环重复N次;两段代码同时运行,要可能要修改某一个变量值,但到底谁可以在什么时间、什么情况改需要精细控制。

    在Go中,为了完成上述程序执行逻辑的控制,需要用的if, for, switch, func, select等内容,逐个来看。

    顺序结构

    Go 默认会按照顺序结构执行,即按照 main() 函数的语句的顺序,一条一条执行,直到执行碰到return 正常退出程序,或遇到程序异常,异常退出程序。当程序执行到函数main() 中遇到另外一个函数时,系统就会将main 函数的执行状态保存到一个叫”栈“的数据结构中,跳出main函数,去执行函数FuncA,在FuncA中遇到函数 FuncB 也一样。等函数FuncB 执行完成后,会返回FuncA,系统会从栈中找到FuncA 的信息并恢复,继续执行FuncA,知道FuncA返回,然后需要执行 main() 函数,直到 main() 完成,"交还"系统控制权。

    函数调用结构.png

    栈是一种数据结构,具有”先进后出“(FILO)这样的特性,更多细节可以参考维基百科

    选择结构

    当任务遇到不同的情况需要不同处理时,就需要用到 if 语句;当然还有其一家兄弟if..else if...else

    if condition{
    
        /* true logic code */
    
    } // condition满足就执行,否则不执行,执行下面的。
    //最为常见的是错误处理,如果有错误就进行错误处理,没错误就继续
    
    if condition{   
    
        /* true logic code */
    
    }else {
    
        /* false logic code */
    
    }// 条件满足就执行true逻辑,否则执行false逻辑。
    
    if conditionA{
    
        /* A condition */ 
    
    }else if conditionB{
    
        /*B condition */    
    
    }else if conditionC{
    
        /*C condition */    
    
    }else{
    
        /* other condition*/
    
    }// 多条件就从上往下逐个执行,如果条件都不满足,就执行else。 
    //还记得90-100评A,80-90评B,...吗?
    

    if else 除了上述用法以外,还可以嵌套执行,就是如下的效果,用于复杂的条件判定。

    if conditionA{
        if conditonA.1 {
            //A.1
        }else {
            //A.x(x!=1)
        }
    }else{
        // !A
    }
    

    上述特性,其他语言一般都会提供,Go中还提供一种方式是if statement;conditon{} 其中 statement 是一个简短的声明或赋值,用于获取condition变量的值。与statement; if conditon{} 区别是什么呢?……作用域。作用域是一种表明变量或函数等可见范围与时间的描述。刚刚if语句中,如果statementif中,则statement中变量作用范围是该 if 块中,一旦离开if,就无法访问。而 statementif 外,则上下文对其变量都可以看到或修改。Go 中有一个原则,与含义不相关的简单变量使用简短名称定义,并且其作用域最小,这样就可以多处重复利用这些简短名称,且不会影响程序执行。最为常见到例子就是错误状态判断,在数据类型中map一节中,我们知道查询一个key会返回两个值,一个值本身,另外一个是值的状态。一个大的程序中有很多错误情况要判断,此时都可以通过 if value, ok := mapName[key];!ok{/*do something*/}

    多选择结构

    除了if...else外,还有一种选择结构叫 switch ,它通过用于很多个情况的选择,用来替代复杂的if...else if...elseif...elseswitch 一般情况下,是后面跟有一个变量,case condition 对变量取值进行判断,满足则执行,执行完当前条件,退出 switch; 如果除 default 外的条件都不满足,那就执行defalut语句,如果没有defalut语句,那就直接跳过 switch。如果一个语句可以应对多个condition,则可以使用逗号,将所有满足条件的罗列出来。

    switch V{
        case conditonA:
            //statement
        case conditonB, conditonC, conditionD:
            //statemtnt
        case conditionE:
            // statement
        default:
            // statement
    }
    

    switch 还有一种特殊情况,就是没有变量 v ,那么变量可以直接在condition中表达,就更像if else

    如果执行完第一个case,还想继续向下判断,那么可以通过 fallthrough 来继续测试后面的case条件,满足则再次执行

    // 成绩score
    switch {
        case score < 60 && score >= 60:
            fmt.Printf("不及格,D\n")
        case score >= 60:
            fmt.Printf("及格,")
            fallthrough
        case score >=70 && score < 80  :
            fmt.Println("C\n")
        case score >=80 && score < 90:
            fmt.Println("B\n")
        case score >=90 && score <= 100:
            fmt.Println("A\n")
        default:
            fmt.Println("录错成绩了,别激动!")
    }
    

    循环结构

    循环,就是重复执行某一个需要重复的工作,例如一堆数据中找最大,或者单纯的想看看这个数据集。如果循环没没有退出条件的限制,最简单后果是CPU使用率一直很高,或者内存被消耗尽,也或者你设计良好,它默默的一直干活,总之设计循环退出条件是一件聪明的事情,可以在任意情况下选择退出,尽管你可以把退出条件设置的比较隐蔽。

    Go 循环只有一种叫做for, 没有其他。for 结构:

    for init;condition;post{
        /* do something */ 
    }
    

    其中 init为变量初始化条件,如果if 一样,在for 结构声明的变量,只能在该块中调用,初始化条件只执行一次;在初始化完成后,for 开始判断condition 是否满足,如果条件满足,则执行循环块;执行完循环后执行post 语句。然后继续检查条件是否满足,重复下去,直到条件不满足,退出循环条件。如果初始化条件声明在for 之外,初始化条件可以跳过;如果post语句在循环中执行,那么post 也可以省略;如果看到下面的循环中断关键字 break,可以用来手动判断是否退出,那么condition 也可以省略。

    中断循环的关键字 break ,当程序执行到循环中的break 语句时,循环退出

    跳出当前循环的关键字 continue, 当程序执行到循环中的 continue 时,循环剩下的语句将被跳过,执行post,开始下一次循环,计算循环条件。

    package main
    
    import  "fmt"
    
    func main() {
        for i := 1; i <= 5; i++ {
            for j := 1; j <= i; j++ {
                if i == 4 && j == 2 {
                    fmt.Printf("\t\t") //当碰到i=4,j=2的时候,则打印两个制表符
                    continue // 跳出本地循环,即不打印下面 2x4=8这一项
                }
                fmt.Printf("%d x %d = %d\t", j, i, i*j)
            }
            fmt.Println()
        }
    }
    /* another
    内部for循环还可以写为
    --------------version 1-------------------
    for i := 1; i <= 5; i++ {
            j := 1
            for ; j <= i; j++ { // 虽然省略了init,但是分号不可省,注意
                if i == 4 && j == 2 {
                    fmt.Printf("\t\t")
                    continue
                }
                fmt.Printf("%d x %d = %d\t", j, i, i*j)
            }
            fmt.Println()
        }
    ---------------version 2 -------------------
    for i := 1; i <= 5; i++ {
            j := 1
            for j <= i {// 支持只有条件的for
                if i == 4 && j == 2 {
                    fmt.Printf("\t\t")
                    continue
                }
                fmt.Printf("%d x %d = %d\t", j, i, i*j)
                j++ // 还是要更新条件变量,否则真的时一直循环,直到死机或被系统强制结束
            }
            fmt.Println()
        }
    --------------version 3 ------------------------
    for i := 1; i <= 5; i++ {
            j := 1
            for { // 如果没有条件就一直循环,直到
                if j > i {
                    break // 直到碰到break,break 可以通过if设置,如果没有if,那就没有循环
                }
                if i == 4 && j == 2 {
                    fmt.Printf("\t\t")
                    continue
                }
                fmt.Printf("%d x %d = %d\t", j, i, i*j)
                j++
            }
            fmt.Println()
        }
    /* Result
    1 x 1 = 1
    1 x 2 = 2       2 x 2 = 4
    1 x 3 = 3       2 x 3 = 6       3 x 3 = 9
    1 x 4 = 4                       3 x 4 = 12      4 x 4 = 16
    1 x 5 = 5       2 x 5 = 10      3 x 5 = 15      4 x 5 = 20      5 x 5 = 25
    */
    

    需要注意的是,break只能退出当前循环,对于如上面嵌套循环,如果全部退出循环,则需要与嵌套循环数量完全一致的break语句。

    这是一般程序结构控制,对于并发或者说多线程程序控制,更加复杂且难以预测。好消息是,Go 有原生并发支持,但不会再这一节展开。

    相关文章

      网友评论

          本文标题:4. Go 程序结构控制

          本文链接:https://www.haomeiwen.com/subject/voqvnftx.html