美文网首页
Go语言学习笔记(五)

Go语言学习笔记(五)

作者: 程序员Morgan | 来源:发表于2019-03-03 17:47 被阅读0次

    这是Go语言学习笔记的第五篇

    条件语句

    条件语句格式:

    if condition {
        statement
    } else {
        statement
    }
    

    几点注意的地方:

    • 不需要使用括号()将条件包含起来

    • 花括号{}必须存在

    • 左花括号{必须与if或else处于同一行

    • if之后,条件语句之前可以添加变量初始化语句, 使用;间隔

    • 在有返回值的函数中,要么每个分支都有 return 语句, 要么最终函数尾带有 return 语句.

    选择语句

    选择语句格式:

    switch i {
        case 0:
            fmt.Println("0")
        case 1:
            fmt.Println("1")
        case 2:
            fmt.Println("2")
        case 3:
            fallthrough
        default:
            fmt.Println("default")
    }
    

    switch几点注意的地方:

    • switch后面的条件表达式不是必需的。

    • 左花括号{必需与switch处于同一行

    • switch的条件表达式不限制为常量或者整数

    • 单个case中可以有多个结果选项

    • 不需要用break来明确退出一个case

    • case后面明确添加fallthrough关键字,则会继续执行紧跟的下一个case

    循环语句

    Go语言中循环语句只支持for关键字,不支持whiledo-while。for基本用法如下:

    sum := 0
    //第一种
    for i:= 0; i < 10; i++ {
        sum += i
    }
    //第二种
    for {
        sum++
        if sum > 100 {
            break
        }
    }
    

    for循环还可以配合range关键字来迭代数组,切片,通道以及集合map中的元素。如下:

    nums := []int{2,3,4}
    for i, num := range nums {
        fmt.Println(i, num)
    }
    

    在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对的 key 值。

    for循环几点需要注意的地方:

    • 左花括号{必须和for处于同一行

    • Go语言中for循环同样支持多重赋值,但Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量

    • for循环同样支持continuebreak来控制循环,但break更高级,可以选择中断哪一个循环。例如:

      for i:=0; i < 5; i++ {
          for j:= 0; j < 10; j++ {
              if j > 5 {
                  break ILoop //此处break终止的是ILoop标签处的外层循环
              }
          } 
      }
      ILoop:
        statement
      

    跳转goto语句

    goto语句表示跳转到本函数的某个标签。如:

    func myfunc() {
        i := 0
        HERE:
        fmt.Println(i)
        i++
        if i < 10 {
            goto HERE
        }
    }
    

    函数

    函数声明

    函数一般按照如下格式声明:func name(parameter-list)(result-list) { body }parameter-list表明函数的参数以及类型,result-list描述函数返回值名字以及类型,函数的返回值不是必须的,但是如果一个函数声明包含返回值,则必须以return语句结尾,除非函数无法运行至结尾,例如函数中有无限循环等情况。下面的4中声明方法都是正确的:

    func add(x int, y int) int {return x+y}
    func sub(x, y int) (z int) {z = x - y; return}
    func first(x int, _ int) int { return x}
    func zero(int, int) int {return 0}
    
    多返回值

    在前面已经了解,Go中函数可以有多个返回值。如果命名了返回值参数,一个没有参数的return语句,会将当前的值作为返回值返回。但如果遇到if等代码块和返回值同名,则需要显式写出返回值。

    func split(sum int) (x, y int) {
        x = sum * 4 / 9
        y = sum - x
        return 
    }
    
    错误处理

    Go语言定义了关于错误处理的标准模式,即error接口。

    error的类型可能是nil或non-nil, nil意味着运行成功,non-nil表示失败。当函数调用返回错误时,一般常用的处理方式:

    1. 最常用的方式是将错误直接返回给调用方。如:

      resp, err := http.Get(url)
      if err != nil {
          return nil, err
      }
      
    2. 重新尝试失败的操作,但是注意限制重试的时间间隔或者重试次数

    3. 如果错误发生,程序无法继续运行,则需要输出错误信息并结束程序

    4. 如果错误不严重,可以直接忽略掉错误。

    看一个例子,例子展示如何从标准输入中读取字符,以及判断文件结束。io包保证任何由文件结束引起的读取失败都返回同一个错误-io.EOF

    in := bufio.NewReader(os.Stdin)
    
    for {
        r, _, err := in.ReadRune()
        if err == io.EOF {
            break
        }
        if err != nil {
            return fmt.Errorf("read failed:%v", err)
        }
    }
    
    匿名函数和闭包

    匿名函数顾名思义,就是不带函数名的函数,在Go语言中,可以随时在代码里定义匿名函数。例如:

    func main() {
        var v func(a int) int
        
        v = func(a int) int {
            return a++
        }
    }
    

    函数可以像普通变量一样被传递或引用。

    闭包和匿名函数不太一样,但是在Go中闭包必须由匿名函数实现。闭包中包含自由变量的代码块,自由变量指的是未绑定到特定对象的变量。同时闭包还包含自由变量当时的环境,即作用域。我们看一个例子:

    func main() {
        var j int = 5
        
        a := func()(func()) {
            var i int = 10
            return func() {
                fmt.Println("i, j: %d, %d\n", i, j)
            }
        }()
        
        a()
        j *= 2
        a()
    }
    

    运行结果是:

    i, j: 10, 5
    i, j: 10, 10 
    

    由于变量a指向的闭包函数引用了局部变量i和j, i的值被隔离,在闭包外不能被修改,改变j的值以后,再次调用a, 发现结果是修改的值。

    需要注意的地方是:下面的代码是有问题的

    var strs []func()
    var strSlice = []string{"Hello", "Learn", "the", "Go", "Language"} 
    for _, str := range strSlice {
        strs = append(strs, func() {
            fmt.Println(str)
        })
    }
    
    for _, str1 := range strs {
        str1()
    }
    

    问题原因在于循环变量的作用域。在第一个for循环中闭包记录的是循环变量str的内存地址,并不是每次循环变量str的值。因此在调用str1()的时候,strs里面其实保存的值已经是循环迭代完的值,即“Language”。

    Go底层实现闭包的原理:其实是闭包返回的函数指针指向的内存区域是一个结构体,该结构体包含了匿名函数的指针和自由变量的指针。

    解决办法就是每次复制变量str然后传到匿名函数中,让闭包的环境变量不相同。即:

    for _, str := range strSlice {
        s := str
        strs = append(strs, func() {
            fmt.Println(s)
        })
    }
    

    关于闭包的解释,这里推荐一篇文章:「Closures in Go」,看完以后感觉对闭包会有更深的理解。

    更多文章可以关注公众号:程序员Morgan.

    欢迎扫码关注

    相关文章

      网友评论

          本文标题:Go语言学习笔记(五)

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