(五)Go函数式编程

函数与闭包

闭包就是指有权访问另一个函数作用域中的变量的函数。闭包由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

正常情况下,外部函数调用完后其内部变量对象就应该被销毁,但闭包的存在使得我们仍然能够访问外部函数的变量对象。

Go语言对函数式编程主要是体现在闭包上面。

函数式编程 vs 函数指针

  • 函数是一等公民:参数,变量,返回值都可以是函数(c++里只有函数指针,Java里函数只是一个名字)
  • 高阶函数:函数的参数可以是一个函数

在这里插入图片描述

  • 局部变量:函数内变量,函数调用结束后释放
  • 自由变量:可以在函数外被调用,是闭包产生的变量

闭包常用的表现形式是:在函数内返回另一个函数

package main
import "fmt"
// 返回一个新函数
func fib() func() int {
    a,b := 0,1 // 声明闭包变量 
    return func() int {
        b,a = a+b,b
        return b
    }
}

func main() {
    f := fib()
    for i := 0 ; i < 20;i++ {
        fmt.Printf("%d ",f())
    }
}
// 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946

其他语言对闭包的支持

Python 中的闭包: python原生支持闭包、使用_closure_来查看闭包内容

# Python闭包
def adder():
      sum = 0
      def  f(value):
            nonlocal sum
            sum += value
            return sum 
      return f

C++ 中的闭包

过去stl或者boost带有类似库;C++11及以后:支持闭包

以下是C++14下编译通过的

auto adder(){
  auto sum = 0;
   return [-] (int value) mutable {
    sum += value;
    return sum;
  }
} 

Java 中的闭包

  • 1.8以前: 匿名类或Lambda表达式均支持闭包

  • 1.8以后: 使用 Function 接口和 Lambda 表达式来创建函数对象 函数本身不能作为参数和返回值的

Function<Integer,Integer> adder() {
  final Holder<Integer> sum = new Holder<>(0);
   return (Integer value) -> {
    sum.value += value;
    return sum.value;
  }
}

函数式编程应用

  • 案例一: 斐波那契数列

    // 斐波那契数列闭包函数
    func Fibonacci() func() int {
        a, b := 0, 1
        return func() int {
            a, b = b, a+b
            return a
        }
    }
    func main() {
        f := fib()
        for i := 0 ; i < 20;i++ {
            fmt.Printf("%d ",f())
        }
    }
    // 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946
    
  • 案例二:为函数实现接口,使用接口实现斐波那契数列结果打印

    // 使用闭包返回 IntGen 类型
    func fib() IntGen {
        a,b := 0,1
        return func() int {
            b,a = a+b,b
            return b
        }
    }
    // 定义接口
    type IntGen func() int
    // 接口方法
    func (g IntGen) Read(p []byte) (n int,err error) {
        // 执行闭包函数,得到结果
        next := g()
        // 添加限制条件
        if next > 1000000 {
            return 0,io.EOF
        }
        // 打印信息
        s := fmt.Sprintf("%d ",next)
        return strings.NewReader(s).Read(p)
    }
    
    // 实现成为一个read的接口
    func PrintFib(read io.Reader) {
        scanner := bufio.NewScanner(read)
    
        for scanner.Scan() {
            // 使用 scanner.Scan(), 像读文件一样打印斐波那契数列。
            fmt.Println(scanner.Text())
        }
    }
    func main() {
        f := fib()
        PrintFib(f)
    } 
    
  • 案例三: 使用函数遍历二叉树

    func (t *TreeNode) Traverse() {
        if t == nil {
            return
        }
    	// 二叉树遍历
        t.Left.Traverse()
        fmt.Println(t.Val)
        t.Right.Traverse()
    }
    

    使用闭包,给二叉树遍历扩展新功能。

    func (t *TreeNode) Traverse() {
        cnt := 0
        // 使用闭包,给遍历的时候添加打印功能
        t.TraverseFunc(func(node *TreeNode) {
            fmt.Printf("%d ",node.Val)
            cnt ++
        })
        // 打印出一共又多少节点
        fmt.Println("\nNode cnt is",cnt)
    }
    
    func (t *TreeNode) TraverseFunc(f func (*TreeNode) ) {
        if t == nil {
            return
        }
    	// 二叉树遍历
        t.Left.TraverseFunc(f)
        f(t)
        t.Right.TraverseFunc(f)
    }
    

Go语言闭包的特点

  • 更为自然,不需要修饰如何访问自由变量
  • 没有 Lambda 表达式,但是又匿名函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值