zoukankan      html  css  js  c++  java
  • Go 函数,包(二)

    #### Go 函数,包(二)
    ***百丈峰,松如浪,地势坤,厚德载物之像***
    今天又到周五啦,你们有没有激动呢,反正我很激动,又有两天的自由了;
    上一节我们学习了Go 的函数和包的一些知识 , 今天接着学习...
    ---
    ##### init 函数
    每个程序源文件都可以包含一个init 函数, 该函数在main 函数前执行,被Go 运行框架调用;
    package main
    
    import "fmt"
    
    func init(){
       fmt.Print("init ")
    }
    func main()  {
       fmt.Print("main")
    }
    

      


    输出结果: init main
    init 函数的注意事项和细节:
    1. 如果一个文件同时包含***全局变量定义,init函数和main 函数***则执行的流程为全局变量定义->init函数->main函数;
    2. 其主要作用是完成一些初始化工作
    3. 如果多个包都有init 函数时,执行顺序为导入顺序,如:
    A 导入了 B , B 导入了 C , 那么执行顺序为先执行C 中的全局变量定义,init 函数,再执行B 包中的, 最后执行A 包中的;
    package main
    
    import "fmt"
    var a = test01()
    func init(){
       fmt.Println("init") // 执行顺序2 
       fmt.Println(a) // 执行顺序3
    }
    func test01() int {
       fmt.Println("test01") // 执行顺序1
       return 1
    }
    func main()  {
       fmt.Println("main") // 执行顺序4
    }
    

      


    ---
    ##### 匿名函数
    Go 支持匿名函数,匿名函数也就是没有名字的函数,如果某一个函数希望使用一次,可以使用匿名函数,同时匿名函数也可以实现多次调用;
    如果将匿名函数赋值给一个全局变量,那么这个匿名函数就成为了一个全局匿名函数,在程序的有效作用域都有效;

    package main
    
    import "fmt"
    
    func main(){
       // 定义时直接调用
       sum := func(a,b int) int {
          return a +b
       }(10,20)
       fmt.Println(sum)
       // 将匿名函数赋值给一个变量,通过变量调用匿名函数
       a := func(a,b int) int {
          return a+b
       }
       sum = a(10,20)
       fmt.Println(sum)
    }
    

      


    ---
    ##### 闭包
    闭包实际上就是一个函数和与其相关的引用环境组合的一个整体
    package main
    
    import "fmt"
    
    func add()func(int)int{
       var n int = 1
       return func(i int) int {
          n += i
          return n
       }
    }
    func main(){
       a := add()
       fmt.Println(a(1)) // 2
       fmt.Println(a(10)) //12
       fmt.Println(a(2)) //14
    }
    

      


    说明:
    1. add 是一个函数,返回的数据类型是一个匿名函数;
    2. 返回的匿名函数 引用到add 函数的变量n ,因此返回的匿名函数就和n 形成一个整体,构成闭包;
    3. 也可以这样理解: 闭包是类,函数是方法,n 是字段,函数和它使用的n 构成闭包;
    4. 因此当反复调用a 函数时, n 将累计;
    package main
    
    import (
       "fmt"
       "strings"
    )
    
    func addStr() func(str string) string {
       var base = "a"
       return func(str string) string {
          base += str
          return base
       }
    }
    // 判断字符串的后缀是否为指定的
    func makeSuffix(suffix string) func(string) string {
       return func(s string) string {
          if !strings.HasSuffix(s,suffix) {
             return s + suffix
          }
          return s
       }
    }
    func main(){
       a := addStr()
       fmt.Println(a("b")) // ab
       fmt.Println(a("c")) // abc
       fmt.Println(a("d")) // abcd
       jpg := makeSuffix(".jpg")
       png := makeSuffix(".png")
       fmt.Println(jpg("a.jpg")) // a.jpg
       fmt.Println(jpg("b"))  //b.jpg
       fmt.Println(png("a.png"))  // a.png
       fmt.Println(png("b"))  //b.png 
    }
    

      


    1. 返回的匿名函数和参数suffix 变量组合成一个闭包,因此传入一次可以多次调用;
    ---
    ##### defer 函数
    defer 也称为延时执行;
    当函数执行到defer 时,暂时不执行,将defer 后的语句压入独立的栈,当函数执行完毕,return 前,再从defer 栈按照先入后出原则方式执行;
    package main
    
    import "fmt"
    
    func add(a,b int) int {
       defer fmt.Println("a=",a)  //10 输出顺序3 , 在将语句放入栈时,相关参数的值会进行拷贝
       defer fmt.Println("b=",b)  //20 输出顺序2 , 在将语句放入栈时,相关参数的值会进行拷贝
       a = 11
       sum := a + b
       defer fmt.Println("sum=",sum) //31  输出顺序1
       return sum
    }
    func main(){
       sum := add(10,20)
       fmt.Println(sum) //31 输出顺序4
    }
    

      


    ***在defer 将语句入到栈时,会将相关参数的值进行拷贝***
    1. 在Go 开发中通常会在创建资源(打开文件,连接数据库,锁资源)后使用defer file.Close() conn.Close()
    2. 在defer 后,仍可以继续使用已经创建的资源;
    ---
    ##### 函数参数传递方式
    上一节中已经讲过函数参数的值类型与引用类型,我们再来深入总结一下,这个知识点在编译型语言中很重要;
    1. 函数参数是值类型时就是值传递,函数参数是引用类型就是引用传递;
    2. 不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是值传递是值的拷贝,引用传递是地址的拷贝;
    3. 一般情况下地址拷贝效率高,而值拷贝由参数的数据大小, 数据越大,效率越低;
    4. 值类型: 基本数据类型int 系列, float 系列, bool, string, 数组,结构体;
    5. 引用类型: 指针,slice 切片, map , chan ,interface ;
    package main
    
    import "fmt"
    
    // 值传递
    func test01(a int){
       fmt.Printf("[test01] a value= %d a address=%p
    ",a,&a)
    }
    // 对于值传递希望更改原来的值可以传入变量地址
    func test03(a *int){
       *a = 100
       fmt.Printf("[test03] c value=%v c address=%p
    ",*a,&a)
    }
    // 引用传递
    func test02(a []int){
       fmt.Printf("[test02] b value=%v b address=%p
    ",a,&a)
    }
    func main(){
       var a int = 10
       fmt.Printf("a value= %d a address=%p
    ",a,&a)
       test01(a)
       var b = []int{1,2}
       fmt.Printf("b value=%v b address=%p
    ",b,&b)
       test02(b)
       var c = 10
       test03(&c)
       fmt.Printf("c value=%v c address=%p
    ",c,&c)
    }
    

      


    ---
    ##### 变量的作用域
    1. 函数内部声明或定义的变量是局部变量,作用域限于函数内部;
    2. 函数外部声明或定义的变量是全局变量,作用域在整个包有效,若首字母大写,则作用域为整个程序;
    3. 如果变量声明或定义在代码块内,如: if/for 代码块内,则作用域仅限于代码块;
    package main
    
    import "fmt"
    
    // 全局变量
    var name = "golang"
    // 如果首字母大写,则在整个程序中有效,其它包也可以使用
    var Age = 22
    
    func test01(){
       // 局部变量
       var a int = 10
       fmt.Println(a)
       // 使用全局变量
       fmt.Println(name)
       if a > 2 {
          // 代码块内部的局部变量, 仅限于if 代码块有效
          var d int = 100
          fmt.Println(d)
       }
       // 代码块内部的局部变量, 仅限于if 代码块有效
       //fmt.Println(d) // error
    }
    func main(){
       // 局部变量
       var b int = 1
       fmt.Println(b)
       // 使用全局变量
       fmt.Println(name)
       test01()
    }
    

      个人微信公众号上有最新文章: 欢迎大家关注一同学习交流

  • 相关阅读:
    LeetCode 109 Convert Sorted List to Binary Search Tree
    LeetCode 108 Convert Sorted Array to Binary Search Tree
    LeetCode 107. Binary Tree Level Order Traversal II
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 103 Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 104. Maximum Depth of Binary Tree
    接口和多态性
    C# 编码规范
  • 原文地址:https://www.cnblogs.com/Mail-maomao/p/11411767.html
Copyright © 2011-2022 走看看