Chinaunix首页 | 论坛 | 博客
  • 博客访问: 643163
  • 博文数量: 363
  • 博客积分: 110
  • 博客等级: 民兵
  • 技术积分: 1347
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-22 16:07
文章分类

全部博文(363)

文章存档

2018年(83)

2016年(1)

2014年(2)

2013年(34)

2012年(236)

2011年(7)

分类: Python/Ruby

2018-01-30 08:47:18

原文地址:Golang之流程与函数 作者:scq2099yt

一、流程控制
        Go语言的流程控制包括3大类:条件判断、循环控制和无条件跳转。
1、if
        Go语言的if条件判断语句中不需要括号,具体如下:
        if x > 10 {
            fmt.Println("x is greater than 10")
        }
        else {
            fmt.Println("x is less than 10")
        }
        上面这些跟其它脚本语言相比没什么出奇的。Go语言的if有一个强大的地方就是条件判断语句里面允许声明一个变量,其作用域只能在该条件逻辑块内:
        //计算获取值x,然后根据x返回的大小,判断是否大于10
        if x := computedValue(); x > 10 {
            fmt.Println("x is greater than 10")
        }
        else{
            fmt.Println("x is less than 10")
        }
        //这个地方如果调用x就会出错,因为x仅仅是条件里的变量
        fmt.Println(x)
2、goto
        这是一条在各种语言中都存在争议的语句,在Go语言里也不例外。用goto只能跳转到当前函数内定义的标签处:
        func myFunc(){
            i := 0
        Here:                //同其它语言也是以冒号结束作为标签
            println(i)
            i++
            goto Here     //跳转到Here去
        }
        标签名是大小写敏感的。
3、for
        Go语言中的for不同于C语言,其既可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作,其语法如下:
        for expression1; expression2; expression3 {
                //...
        }
        expression1expression3是变量声明或者函数调用返回值之类的,expression2是条件判断表达式,expression1在循环开始前调用,expression3在每轮循环结束之时调用。举例说明如下:
        package main
        import "fmt"
        func main(){
            sum := 0;
            for index:=0; index<10; index++ {
                sum += index
            }
            fmt.Println("sum is equal to ", sum)
        }
        在Go语言中,for的表达式1和3是可以忽略的,那么就会简化成while形式了,具体如下:
        sum := 1
        for sum < 1000 {
            sum += sum
        }
        在循环中,一般都会有两个重要的操作即break和continue,break跳出当前循环,continue跳过本次循环。这都很稀松平常,干货来了,当循环嵌套过深时,break可以配合标签使用,即跳转至标签所指定的位置,以此来跳到多重循环中的外层循环。
        for配合range可以用于读取slice和map的数据:
        for k, v:=range map {
            fmt.Println("map's key: ", k)
            fmt.Println("map's val: ", v)
        }
        由于Go语言支持“多值返回”,对于“声明而为被调用”的变量,编译器会报错,在这种情况下,可以使用_来丢弃不需要的返回值,如下所示:
        for _, v := range map {
            fmr.Println("map's val:",  v)
        }
4、switch
        switch与C语言类似,语法如下:
        switch sExpr {
        case expr1:
            some instructions
        case expr2:
            some other instructions
        case expr3:
            some other instructions
        default:
            other code
        }        
         sExpr和expr1、expr2、expr3的类型必须一致。Go语言的switch非常灵活,表达式不必是常量或整数,执行过程从上至下,直到找到匹配项;而如果switch没有表达式,它会匹配true。Go语言里面switch默认相当于每个case最后自动带有break,但是如果在case语句后使用fallthrough会强制执行后面的case代码。
        integer := 5
        switch integer {
        case 4:
            fmt.Println("The integer was <= 4")
            fallthrough
        case 5:
            fmt.Println("The integer was <= 5")
            fallthrough
        case 6:
            fmt.Println("The integer was <= 6")
        default:
            fmt.Println("default case")            
        }
        上面程序输出结果如下:
        The integer was <= 5
        The integer was <= 6
        default case
        

二、  函数    
   
         函数是Go语言里面的核心设计,其通过关键字func来声明,具体格式如下:
        func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
            //逻辑代码
            //返回多个值
            return value1, value2
        }
        函数相邻参数类型一致,则前一个参数可以省略类型,参数变量名在类型前,如果没有返回值的话可以忽略输出参数声明部分即output,输出参数可以只给出类型,也可以给出变量名,给出变量名后return时可以省略返回的变量列表,但是不建议这么做,这种情况程序可读性很差。
        package main
        package "fmt"
        func max(a, b int)  int {
            if a > b {
                return a
            }
            return b
        }
1、多值返回
        Go语言比C语言先进的特性在于提供了多值返回,这点同Lua,举例如下:
        package main
        import "fmt"
        //返回A+B和A*B
        func SumAndProduct(A, B int) (int, int) {
            return A+B, A*B
        } 
        func main() {
            x := 3
            y := 4
            xPLUSy, xTIMESy := SumAndProduct(x, y)
            fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
            fmt.Printf("%d + %d = %d\n", x, y, xTIMESy)
        }       
2、变参
        Go语言函数支持变参,具体语法如下:
        func myfunc(arg ...int) {}
        arg ...int表示函数接受类型为int的不定数量的参数。在函数体中,变量arg是一个slice。
        for _, n := range arg {
            fmt.Printf("And the number is: %d\n", n)
        }        
3、传值与传指针
        这点与C语言一致,在此对于传指针说明以下几点:
        (1)传指针使得多个函数能操作同一个对象。
        (2)传指针比较轻量级即只传内存地址。这在传递体积比较大的结构体时,可以节省传值时copy的系统开销(虽然也需要copy地址)。
        (3)Go语言中string、slice、map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。需要注意的是:若函数需要改变slice的长度,则仍需要取地址传递指针。
4、defer       
        Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然容易造成资源泄露等问题。这里以打开文件为例,传统方式如下:
        func ReadWrite() bool {
            file.Open("file")
            if failureX {
                file.Close()
                return false
            }
            if failureY {
                file.Close()
                return false
            }
    
            file.Close()
            return true

        } 
        上面这段代码如果采用defer则会优雅得多:
        func ReadWrite() bool {
            file.Open("file")
            defer file.Close()
            if failureX {
                return false
            }
            if failureY {
                return false
            }
            return true
        }
        在defer后指定的函数会在函数推出前调用,所以非常优雅。如果有很多调用defer,则defer采用后进先出模式,所以如下代码会输出4 3 2 1 0:
        for i := 0; i < 5; i++ {
            defer fmt.Printf("%d ", i)
        }
5、第一类值
        在Go语言中函数也是一种变量,这点与Lua中的函数类似,是作为第一类值的。我们可以通过type来定义它,其类型就是所有拥有相同的参数,相同的返回值:
        type typename func(input1 inputType1, input2 inoutType2 [, ...]) (result1 resultType1 [, ...])
        函数作为类型的好处就是可以把函数当做值来传递:
        package main
        import "fmt"
        type testInt func(int) bool        //声明了一个函数类型
        func isOdd(integer int) bool {
            if integer%2 == 0 {
                return false
            }
            return true
        }
        func isEven(integer int) bool {
            if integer%2 == 0 {
                return true
            }
            return false
        }
        //声明的函数类型在这个地方当做了一个参数
        func  filter(slice []int, f testInt) []int {
            var result []int
            for _, value:= range slice {
                if f(value) {
                    result = append(result, value)
                }
            }
            return result
        }
        func main(){
            slice := []int {1, 2, 3, 4, 5, 7}
            fmt.Println("slice = ", slice)
            odd := filter(slice, isOdd)        //函数当做值来传递了
            fmt.Println("Odd elements of slice are: ", odd)
            even := filter(slice, isEven)     //函数当做值来传递了
            fmt.Println("Even elements of  slice are: ", even)
        }
        函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到testInt这个类型是一个函数类型,两个filter函数的参数和返回值与testInt类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常灵活。
6、Panic和Recover
        Go语言没有像Java那样的异常处理机制,即不能抛出异常,而是使用了panic和recover机制。
        (1)Panic
        Panic是一个可以中断原有控制流程的内建函数。当函数F调用panic,则函数F的执行被中断,但是F中的延迟函数defer会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上直到发生panic的goroutine中所有调用的函数返回,此时程序退出。
        (2)Recover
        Recover是一个内建函数,其能让产生异常的goroutine恢复过来。recover仅在延迟函数中有效。在正常执行过程中调用recover会返回nil,并且没有其它任何效果。在goroutine发生异常时,调用recover可以捕获到panic的输入值,并且恢复正常的执行。
        下面函数演示如何使用panic:
        var user = os.Getenv("USER")
        func inti() {
            if user == "" {
                panic("no value for $USER")
            }
        }                
        下面函数检查作为其参数的函数在执行时是否会产生panic:
        func throwsPanic(f func()) (b bool) {
            defer func() {
                if x := recover(); x != nil {
                    b = true
                }
            } ()
            f()     //执行函数f,如果f中出现了panic,那么就可以恢复回来
            return
        }
7、main函数和init函数
        Go语言里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数定时不能有任何参数和返回值。Go语言程序会自动调用init()和main(),前面是可选的,但后者是package main必须的。 
        程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么编译时就会将它们依次导入,有时候一个包被多个包同时导入,那么它只会被导入一次。当一个包被导入时,如果该包还导入了其它包,则会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。          
8、import
        在Go语言代码中一般用import来导入包文件:
        imort(
             "fmt"
        )     
        然后在代码中就可以如下方式来调用包中函数:
        fmt.Println("hello world")   
        fmt是Go语言的标准库,其实是去goroot下加载该模块。Go语言的import还支持如下两种方式来加载自己写的模块:
        (1)相对路径
         import "./model"      //当前文件同一目录的model目录
        (2)绝对路径
        import "shorturl/model"           //加载gopath/src/shorturl/model模块
        除了上面几种import方式外,还有一些特殊的import形式:
        (1)点操作
        import(
               . "fmt"
        )
        用点操作导入包后,调用包中函数时可以省略前缀的包名,也就是前面的调用可以省略成Println("hello world")。
        (2)别名操作
        别名操作就是可以将包命名为另一个容易记忆的名字:
        import(
             f "fmt"
        )
        这样前面调用也可以这样了f.Println("hello world")。
        (3)_操作
        import(
             "database/sql"
             _ "github.com/ziutek/mysql/godir"
        )
        _操作其实是引入该包,不直接使用包里的函数,而是调用了该包里面的init函数。








阅读(453) | 评论(0) | 转发(0) |
0

上一篇:Golang之语言基础

下一篇:Golang之struct类型

给主人留下些什么吧!~~