Go语言学习笔记十四--面向接口-函数式编程

函数式编程概念

在函数式编程中,应用最多的就是闭包。

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
----维基百科

简单来说,闭包就是在函数内使用外部自由变量。

函数式编程的特点:

  • 函数是一等公民
  • 高阶函数(函数的参数和返回值都可以是函数)
  • 闭包

重点介绍一下闭包:
闭包结构图

可以看到,在函数体类,允许自由变量进入,而自由变量又可以与其他变量引用,形成一条链,这样的结构就是闭包。

其他语言的函数式编程

  • 在Python中闭包写法:
    Python中的闭包
    可以看到,在Python中如果要使用闭包,自由变量需要进行声明。

  • C++中的闭包写法:
    C++中闭包
    C++在高级版本中,可以开始原生支持闭包

  • Java中的闭包写法:
    Java中的闭包
    Java只有在1.8版本可以使用function类,进行闭包操作。这也是增加的新特性,但是其实Function,并不是一个函数,它是一个类。且在使用闭包时是不能使用基础类型的,需要我们自己定义一个应用类型,才可以当做,自由变量。

GO语言中函数式编程

GO语言中因为是新诞生的语言,所以对于函数式编程进行原生支持,且函数为一等公民,下面通过几个列子来看,GO中的函数式编程。

事例1:累加器

package main

import "fmt"

func Adder()func(int)int  {
	num:=0
	return func(i int) int {
		num+=i //num为自由变量
		return num
	}
}

func main() {
	adder := Adder()
	for i := 0; i<10;i++  {
		fmt.Printf("0+...+%d=%d\n",i,adder(i))
	}
}

打印:

0+...+0=0
0+...+1=1
0+...+2=3
0+...+3=6
0+...+4=10
0+...+5=15
0+...+6=21
0+...+7=28
0+...+8=36
0+...+9=45

事例2:斐波那契数列

解释一下斐波那契数列,其实就是每个数字,都是由前两个数字累加而得到。

/**
	函数生成斐波那契数列(每个数字都是由前两个数字相加得到)
 */
func fibonacci() func () int {
	a, b := 0, 1
	return func() int {
		//在这里,生成关键
		// 1 1 2 3 5 8
		//   a b
		//	   a b
		a, b = b, a+b
		return a
	}
}
func main() {
	fun := fibonacci()
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
	fmt.Println(fun())
}

打印:

1
1
2
3
5
8
13
21
34
55

改造斐波那契数列函数

我们希望像读文件一样来,让其自动生成。所以就需要实现read接口:

将之前的打印函数拿过来:

func printContentFile(reader io.Reader) {
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		println(scanner.Text())
	}
}

想直接调用printContentFile方法就自动生成,所以传入的必须是reader接口类型。

改造,定义一个type:函数。使其实现Reader接口。

//函数实现接口
//定义一个函数,使用type修饰。可以实现接口,也就是说只要是被type修饰的东西都可以实现接口。
type IntGen func() int
//实现read接口
func (g IntGen) Read(p []byte) (n int, err error) {
	next := g()

	if next > 10000 { //这里因为不设置退出会一直打印下去,所以做了限制
		return 0, io.EOF
	}
	s := fmt.Sprintf("%d\n", next)
	return strings.NewReader(s).Read(p)
}

函数返回为刚定义的函数。

/**
	函数生成斐波那契数列(每个数字都是由前两个数字相加得到)
 */
func fibonacci() IntGen {
	a, b := 0, 1
	return func() int {
		//在这里,生成关键
		// 1 1 2 3 5 8
		//   a b
		//	   a b
		a, b = b, a+b
		return a
	}
}

调用:

func main() {

	fun := fibonacci()

	printContentFile(fun)
}

打印:

1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765

通过这个例子我们发现,在go语言中,函数也可以实现接口。也可以理解了函数为‌一等公民概念。

事例3:修改tree.Node

通过函数式编程,改造tree.Node的Traverse方法,使其更具有一般性,因为我们原来在定义的时候,方法体类只做了打印操作,而可能业务上可能会做很多事情,而且在编写方法时还不知道调用者会做些什么事情。


//通过函数式编程,使traverse更具有扩展性
func (node *Node)FunTraverse(f func(node *Node)) {//传入一个函数作为参数
	if node == nil {
		return
	}
	node.Left.FunTraverse(f)
	f(node)
	node.Right.FunTraverse(f)
}

调用:


func main() {
	root := tree.Node{Value: 5}
	root.Left = &tree.Node{Value: 1}
	root.Right = &tree.Node{Value: 2}
	root.Left.Right= &tree.Node{Value: 3}
	root.Right.Left = &tree.Node{Value: 4}
	//正常调用
	root.Traverse()

  //函数式方法 打印信息
	root.FunTraverse(func(node *tree.Node) {
		node.Print()
	})
	fmt.Println("-----------")
	//函数式方法 计数
	rootCount := 0
	root.FunTraverse(func(node *tree.Node) {
		rootCount++
	})
}

打印:

1 3 5 4 2 
-----------
1 3 5 4 2 
Traverse counts : 5

看到是没有问题的。且比之前的Traverse更加灵活了。


作者所有的学习源码在 go学习源码github地址,如果觉得有用的话帮小智贡献一个star?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值