Go基础:函数、闭包、递归

简介: Go基础:函数、闭包、递归

 目录

函数

GO函数特点:

Go函数声明:

GO函数参数

GO函数返回值

匿名函数

闭包

Go的闭包

递归函数

斐波那契数列(Fibonacci)


函数

GO函数特点:

    • 无需声明原型。
    • 支持不定 变参
    • 返回值类型写在最后面,支持多返回值
    • 支持命名返回参数。
    • 支持匿名函数和闭包
    • 函数也是一种类型,一个函数可以赋值给变量
    • 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。
    • 不支持 重载 (overload)
    • 不支持 默认参数 (default parameter)、可选参数
    • 参数传递:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝。
    • 使用关键字 func 定义函数,左大括号依旧不能另起一行

    Go函数声明:

    package main
    import "fmt"
    func test(fn func() int) int {
        return fn()
    }
    // 定义函数类型。
    type FormatFunc func(s string, x, y int) string 
    func format(fn FormatFunc, s string, x, y int) string {
        return fn(s, x, y)
    }
    func main() {
        s1 := test(func() int { return 100 }) // 直接将匿名函数当参数。
        s2 := format(func(s string, x, y int) string {
            return fmt.Sprintf(s, x, y)
        }, "%d, %d", 10, 20)
        println(s1, s2)
    }
    //  返回结果
    //  100 10, 20

    image.gif

    GO函数参数

    函数可以通过两种方式来传递参数:

      • 值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
      • 引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
      • 在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

      注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。

      注意2:map、slice、chan、指针、interface默认以引用的方式传递。

      任意类型的不定参数: 就是函数的参数和每个参数的类型都不是固定的。

      用interface{}传递任意类型数据是Go语言的惯例用法,而且interface{}是类型安全的。

      func myfunc(args ...interface{}) {
        }

      image.gif

      使用 slice 对象做变参时,必须展开。(slice...)

      package main
      import (
          "fmt"
      )
      func test(s string, n ...int) string {
          var x int
          for _, i := range n {
              x += i
          }
          return fmt.Sprintf(s, x)
      }
      func main() {
          s := []int{1, 2, 3}
          res := test("sum: %d", s...)    // slice... 展开slice
          println(res)
      }

      image.gif

      GO函数返回值

        • "_"标识符,用来忽略函数的某个返回值
        • Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用
        • 返回值的名称应当具有一定的意义,可以作为文档使用。

        匿名函数

        匿名函数是指不需要定义函数名的一种函数实现方式。

        在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

        匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

        package main
        import (
            "fmt"
            "math"
        )
        func main() {
            getSqrt := func(a float64) float64 {
                return math.Sqrt(a)
            }
            fmt.Println(getSqrt(4))
        }
        // 返回
        // 2

        image.gif

        上面先定义了一个名为getSqrt 的变量,初始化该变量时和之前的变量初始化有些不同,使用了func,func是定义函数的,可是这个函数和上面说的函数最大不同就是没有函数名,也就是匿名函数。这里将一个函数当做一个变量一样的操作。

        Golang匿名函数可赋值给变量,做为结构字段,或者在 channel 里传送。

        package main
        func main() {
            // --- function variable ---
            fn := func() { println("Hello, World!") }
            fn()
            // --- function collection ---
            fns := [](func(x int) int){
                func(x int) int { return x + 1 },
                func(x int) int { return x + 2 },
            }
            println(fns[0](100))
            // --- function as field ---
            d := struct {
                fn func() string
            }{
                fn: func() string { return "Hello, World!" },
            }
            println(d.fn())
            // --- channel of function ---
            fc := make(chan func() string, 2)
            fc <- func() string { return "Hello, World!" }
            println((<-fc)())
        }

        image.gif

        输出结果:

        Hello, World!
            101
            Hello, World!
            Hello, World!

        image.gif

        闭包

        20200223174831398.png

        image.gif

        闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

        20200223172043881.png

        函数与闭包image.gif

        Go的闭包

        package main
        import (
          "fmt"
        )
        func a() func() int {
          i := 0
          b := func() int {
            i++
            fmt.Println(i)
            return i
          }
          return b
        }
        func main() {
          c := a()
          c()
          c()
          c()
          fmt.Println("a() //不会输出i")
          a() //不会输出i
          c2 := a()
          c2()
          c2()
          c2()
          //输出结果
          //1
          //2
          //3
          //a() //不会输出i
          //1
          //2
          //3
        }

        image.gif

        这段代码有两个特点:

        函数b嵌套在函数a内部 函数a返回函数b 这样在执行完var c=a()后,变量c实际上是指向了函数b(),再执行函数c()后就会显示i的值,第一次为1,第二次为2,第三次为3,以此类推。 其实,这段代码就创建了一个闭包。因为函数a()外的变量c引用了函数a()内的函数b(),就是说:

        当函数a()的内部函数b()被函数a()外的一个变量引用的时候,就创建了一个闭包。 在上面的例子中,由于闭包的存在使得函数a()返回后,a中的i始终存在,这样每次执行c(),i都是自加1后的值。 从上面可以看出闭包的作用就是在a()执行完并返回后,闭包使得go的垃圾回收机制GC不会收回a()所占用的资源,因为a()的内部函数b()的执行需要依赖a()中的变量i。

        在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所创建所生成的值在下次函数调用时仍然存在。正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中

        下面来想象另一种情况,如果a()返回的不是函数b(),情况就完全不同了。因为a()执行完后,b()没有被返回给a()的外界,只是被a()所引用,而此时a()也只会被b()引 用,因此函数a()和b()互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC回收。所以直接调用a();是页面并没有信息输出。

        下面来说闭包的另一要素引用环境。c()跟c2()引用的是不同的环境,在调用i++时修改的不是同一个i,因此两次的输出都是1。函数a()每进入一次,就形成了一个新的环境,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。这和c()和c()的调用顺序都是无关的。

        递归函数

        递归,就是在运行的过程中调用自己。 一个函数调用自己,就叫做递归函数。

        构成递归需具备的条件:

        1.子问题须与原始问题为同样的事,且更为简单。
            2.不能无限制地调用本身,须有个出口,化简为非递归状况处理。

        image.gif

        斐波那契数列(Fibonacci)

        这个数列从第3项开始,每一项都等于前两项之和。

        package main
        import "fmt"
        func fibonaci(i int) int {
            if i == 0 {
                return 0
            }
            if i == 1 {
                return 1
            }
            return fibonaci(i-1) + fibonaci(i-2)
        }
        func main() {
            var i int
            for i = 0; i < 10; i++ {
                fmt.Printf("%d\n", fibonaci(i))
            }
        }

        image.gif

        输出结果:

        0
            1
            1
            2
            3
            5
            8
            13
            21
            34

        image.gif


        目录
        相关文章
        |
        7月前
        |
        缓存 算法 搜索推荐
        递归函数就这么简单!通俗的Go语言递归指南
        递归函数就这么简单!通俗的Go语言递归指南
        39 0
        |
        12月前
        |
        JavaScript 前端开发 Java
        Go中的闭包、递归
        Go中的闭包、递归
        45 1
        |
        Go 索引
        Golang语言(打卡✏️第三天)map、递归、报错和练习题|Go主题月
        Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。 Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
        166 0
        |
        安全 Java Go
        Go基础:函数、闭包、递归
        Go基础:函数、闭包、递归
        148 0
        Go基础:函数、闭包、递归
        |
        Go 开发工具 git
        GO语言学习笔记(三) - 递归查找目录及子目录下的文件
        GO语言学习笔记(三) - 递归查找目录及子目录下的文件 递归查找目录及子目录下的文件递归查找文件夹及子文件夹下的文件 代码 package main import ( "fmt" "io/ioutil" "os" "strings" ) // 查找目录及子目录.
        3109 0
        |
        4天前
        |
        监控 算法 Go
        Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
        【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
        29 0
        |
        1天前
        |
        JavaScript 前端开发 Go
        Go语言的入门学习
        【4月更文挑战第7天】Go语言,通常称为Golang,是由Google设计并开发的一种编程语言,它于2009年公开发布。Go的设计团队主要包括Robert Griesemer、Rob Pike和Ken Thompson,这三位都是计算机科学和软件工程领域的杰出人物。
        9 1
        |
        1天前
        |
        Go
        |
        2天前
        |
        分布式计算 Java Go
        Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
        【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
        131 1
        |
        2天前
        |
        算法 关系型数据库 MySQL
        Go语言中的分布式ID生成器设计与实现
        【5月更文挑战第6天】本文探讨了Go语言在分布式系统中生成全局唯一ID的策略,包括Twitter的Snowflake算法、UUID和MySQL自增ID。Snowflake算法通过时间戳、节点ID和序列号生成ID,Go实现中需处理时间回拨问题。UUID保证全局唯一,但长度较长。MySQL自增ID依赖数据库,可能造成性能瓶颈。选择策略时需考虑业务需求和并发、时间同步等挑战,以确保系统稳定可靠。
        109 0