人们常说面向对象编程和面向过程编程,随着硬件性能的提升以及编译技术和虚拟机技术的改进,一些曾被性能问题所限制的动态语言开始受到关注,Python、Ruby 和 Lua 等语言都开始在应用中崭露头角。动态语言因其方便快捷的开发方式成为很多人喜爱的编程语言,伴随动态语言的流行,函数式编程也再次进入了我们的视野。
那究竟什么是函数式编程呢?
简单来说,函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。
函数式编程的一个重要特性就是闭包和高阶函数。
一、闭包的概念与价值
在函数式编程模型中经常使用闭包,那什么是闭包?为什么要使用闭包?它能解决什么问题?
func test(s string)(func()){
return func(){
fmt.Println(s)
}
}
func main(){
f:=test("test")
f()
}
test()函数返回了一个函数,返回的这个函数f就是闭包,f自身是没有定义变量的,而是引用了它所在的环境(函数test)中的变量s。当每次在main函数中调用test函数传入不同的参数时,就会形成新的环境。在闭包函数中,函数都是同一个函数,环境却是引用不同的环境。
闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码快或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含在代码块中,所以这些自由变量以及它们引用的对象的没有被释放)为自由变量提供绑定的计算环境(作用域)。
闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。所谓引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。其中的约束是指一个变量的名字和其所代表的对象之间的联系。那么为什么要把引用环境与函数组合起来呢?这主要是因为在支持嵌套作用域的语言中,有时不能简单直接地确定函数的引用环境。这样的语言一般具有这样的特性:
- 函数是一等公民(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
- 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
在函数式编程的过程中,函数是一等公民,变量、参数、返回值都可以是函数,函数的参数也可以是函数,因此是满足高级函数条件。高阶函数是至少满足下列一个条件的函数:
- 接受一个或多个函数作为输入
- 输出一个函数
func Adder()(func(int) int){
sum:=0
return func(a int)int{
sum=a+sum
return sum
}
}
func main(){
f:=Adder()
for i:=1;i<5;i++{
fmt.Println(f(i))
}
fmt.Println("param exit")
}
输出结果为:
1
3
6
10
每次通过f来调用闭包函数时,由于在函数Adder内部的变量sum会在每次闭包函数调用时会保存局部变量sum的值,每次虽然调用相同的函数,但是环境发生了改变。
闭包的价值在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,就是说这些函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。
三、 闭包使用
闭包经常用于回调函数,当IO操作(例如从网络获取数据、文件读写)完成的时候,会对获取的数据进行某些操作,这些操作可以交给函数对象处理。
type gosort func(inter interface{})error
func mysort(slice []int,r gosort)error{
return r(slice)
}
func ascSort(e interface{})error{
s,ok:=e.([]int)
if !ok{
return errors.New("interface Transformation fail")
}
sort.Ints(s)
return nil
}
func descSort(e interface{})error{
s,ok:=e.([]int)
if !ok{
return errors.New("interface Transformation fail")
}
sort.Sort(sort.Reverse(sort.IntSlice(s)))
return nil
}
func main(){
s:=[]int{2,6,9,1,3,8,7}
mysort(s,ascSort)
mysort(s,descSort)
fmt.Println(s)
fmt.Println("param exit")
}
可以根据情况不同分别调用同一个函数,传入的函数作为参数的函数不同来实现升序或者降序排列。