函数式编程的概念
1. 函数式编程 VS 函数指针
-
函数中参数、变量、返回值都可以是函数;
-
高价函数:函数的形参列表或返回参数列表中存在数据类型为函数类型
-
闭包
-
”正统“函数式编程(不要求)
不可变性:不能有状态,只有常量和函数,没有变量、选择语句、循环语句等;
函数只能有一个参数;
注
C++中存在函数指针,java中函数仅仅是名字;
闭包的概念
-
闭包的结构如图所示:
在函数体中分为局部变量和自由变量;
局部变量和自由变量以及自由变量衍生的变量组合为闭包;
-
闭包的样例如下所示:
package main
import "fmt"
// 函数式编程,返回值是函数
func adder() func(int) int {
sum := 0
return func(v int) int {
// 变量sum不是内嵌函数定义的参数,而是由外部函数adder()定义的局部变量
// 对于内嵌函数而言,变量sum是其所处的环境
// func函数返回是一个闭包,包括闭包中的自由变量即计算sum的每个数值
sum += v
return sum
}
}
func main() {
a := adder()
// 对0到9数进行累加,并输出累加的每步结果
for i := 0; i < 10; i++ {
fmt.Printf("0 + 1 + ... + %d = %d\n",
i, a(i))
}
}
- 输出结果
0 + 1 + ... + 0 = 0
0 + 1 + ... + 1 = 1
0 + 1 + ... + 2 = 3
0 + 1 + ... + 3 = 6
0 + 1 + ... + 4 = 10
0 + 1 + ... + 5 = 15
0 + 1 + ... + 6 = 21
0 + 1 + ... + 7 = 28
0 + 1 + ... + 8 = 36
0 + 1 + ... + 9 = 45
-
闭包的样例的结构图如下图所示:
返回值是函数时,返回的是一个闭包;
局部变量v,和自由变量sum以及sum过程值都保存在函数中;
-
其他语言的闭包
-
python中的闭包
原生支持闭包;
使用_closure_来查看闭包内容
def adder(): sum = 0 def f(value): nonlocal sum sum += value return sum return f
-
C++中的闭包
过去:stl或者boost带有类似库
c++11及以后:支持闭包
auto adder() { auto sum = 0; return [=] (int value) mutable { sum += value; return sum; } }
-
Java中的闭包
java中函数不能作为参数和返回值;
1.8以后:使用Function接口(用对象模拟函数)和lambda表达式来创建函数对象;
匿名类和Lambda表达式均支持闭包;
Function<Integer, Integer> adder(){ final Holder<Integer> sum = new Holder<>(0); // sum值不能被改变,故自定义一个类Holder,Holder中包含数据value return (Integer value) -> { sum.value += value; return sum.value; }; }
闭包的应用
- 例一:斐波那契数列
package main import "fmt" // 生成器 // 0, 1, 1, 2, 3, 5, 8, 13, 21, ... // a, b . . . . . . . // a, b . . . . . . // a, b . . . . . // a, b . . . . // a, b . . . // a, b . . // a, b . // a, b func fibonacci() func() int{ a, b := 0, 1 return func() int { a, b = b, a + b return a } } func main() { f := fibonacci() // 多次调用 fmt.Println(f()) // 1 fmt.Println(f()) // 1 fmt.Println(f()) // 2 fmt.Println(f()) // 3 fmt.Println(f()) // 5 fmt.Println(f()) // 8 fmt.Println(f()) // 13 fmt.Println(f()) // 21 }
-
输出结果;
1 1 2 3 5 8 13 21
-
例二:为函数实现接口
定义函数类型作为接口;
package main import ( "bufio" "fmt" "io" "strings" ) func fibonacci() intGen{ a, b := 0, 1 return func() int { a, b = b, a + b return a } } type intGen func() int // 函数实现接口 func (g intGen) Read( p []byte) (n int, err error){ next := g() if next > 1000 { // 设置斐波那契数列数值上限 return 0, io.EOF } s := fmt.Sprintf("%d\n", next) // TODO: incorrect if p is too small! return strings.NewReader(s).Read(p) } func printFileContents(reeder io.Reader){ scanner := bufio.NewScanner(reeder) for scanner.Scan(){ fmt.Println(scanner.Text()) } } func main() { f := fibonacci() // f是intGen类型 printFileContents(f) }
-
例三:使用函数实现二叉树中序遍历
package main import ( "fmt" ) type Node struct { Value int Left, Right *Node } func (node Node) Print() { fmt.Println(node.Value) } // 实现中序变量过程中对节点的操作 // f为自定义的函数 // 可以是Print函数或者其他的函数 func (node *Node) TraverseFunc(f func(*Node)){ if node == nil { return } node.Left.TraverseFunc(f) f(node) node.Right.TraverseFunc(f) } func main() { var root Node root = Node{Value: 3} root.Left = new(Node) root.Right = &Node{5, nil, nil} root.Left.Right = &Node{2, nil, nil} root.Right.Left = &Node{4, nil, nil} // 以中序变量顺序打印节点的value值 root.TraverseFunc(func(node *Node) { node.Print() }) fmt.Println() // 通过中序变量书节点个数 nodeCount := 0 root.TraverseFunc(func(node *Node){ nodeCount++ }) fmt.Println("Node count:", nodeCount) }
- 输出结果
0
2
3
4
5
Node count: 5
Golang闭包小结
-
更为自然,不需要修饰如何访问自由变量;
-
没有Lambda表达式,但有匿名函数;