构造一层又一层的抽象来处理(并隐藏)琐碎的细节。随着硬件能力的提高,将越来越多的任务转嫁给语言和运行时。
函数式思维的好处之一,是能够将低层次细节(如垃圾收集)的控制权移交给运行时,从而消弭了一大批注定会发生的程序错误。
不管层次高低,抽象的目的总是一样的:让开发者从繁琐的运作细节里解脱出来,去解答问题中非重复性的那些方面。
函数式语言向语言和运行时让渡控制权,让开发者抛开负累,投入到更有意义的问题中去。
函数式语言向语言和运行时让渡控制权的途径——迭代让位于高阶函数
用map
等函数替换了迭代。如果能够用高阶函数把希望执行的操作表达出来,语言将会把操作安排得更高效,甚至只要增加一行par
修饰,就能够让操作并行化。
多线程代码属于最难编写,最容易出错,也最难调试的类别。只有卸下线程管理这份头痛的差事,开发者才能少一些低层次的琐碎操劳。
理解掌握的抽象层次永远要比日常使用的抽象层次更深一层
程序员的工作效率依赖于抽象层。抽象隐藏了繁杂的细节,只是有时候会连同重要的考虑因素一起隐藏掉。
开发者不应该抛开所有的责任,不去理解低层次抽象的来龙去脉。使用抽象必须清楚可能产生的连带后果,理解底层的抽象细节才能写出高性能的代码。
当你掌握了背后的原理,才能把力量用在最正确的地方。
函数式语言向语言和运行时让渡控制权的途径——闭包
闭包(closure)是所有函数式语言都具备的一项平常特性。
闭包,实际上是一种特殊的函数,它在暗地里绑定了函数内部引用的所有变量。这种函数(或方法)把它引用的所有东西都放在一个上下文里“包”了起来。
函数式语言向语言和运行时让渡控制权的途径——闭包——Groovy代码演示闭包的创建和绑定
带有
amount
参数的paidMore
函数,其返回值是一个以Employee
实例为参数的代码块,或者叫闭包。类型声明Employee
可写可不写,这里写出来顺便起到文档的作用。给代码块传入参数值100 000,并赋予
isHighPaid
的名称,于是数值100 000就随着这一步赋值操作,永久地和代码块绑定在一起了。以后有员工数据被代入这个代码块求解的时候,它就可以拿绑定的数值作为标准去评判员工的工资高低。
执行闭包:
闭包在生成的时候,会把引用的变量全部圈到代码块的作用域里,封闭、包围起来(故名闭包)。
闭包的每个实例都保有自己的一份变量取值,包括私有变量也是如此。可以创建paidMore
闭包的另一个实例,给它绑定另外的数值(当然实例的名字也要另取)
绑定另一个闭包:
闭包经常被函数式语言和框架当作一种异地执行的机制,用来传递待执行的变换代码,如map()
之类的高阶函数。在缺乏闭包特性的旧版Java平台上,Functional Java利用匿名内部类来模仿“真正的”闭包的某些行为,但语言的先天不足导致这种模仿是不彻底的。
函数式语言向语言和运行时让渡控制权的途径——闭包——原理
makeCounter()
函数首先定义一个局部变量(local_variable
),接着返回一个使用了该局部变量的代码块。注意
makeCounter()
函数的返回类型是Closure
,而不是一个单纯的值。代码块的工作仅仅是递增并返回其局部变量的值。方法中两次明确写出了return
关键字,其实这两个地方Groovy都允许省略。
调用代码块的时候用到了Groovy提供的语法糖衣,即在代码块变量名后直接跟一对圆括号的写法(否则应该写成c1.call()
)。
虽然局部变量不是在代码块里面定义的,但只要代码块引用了该变量,两者就被绑定在一起,这种联系在代码块实例的全部生命期内都一直保持着。
从实现的角度来说,代码块实例从它被创建的一刻起,就持有其作用域内一切事物的封闭副本。当代码块实例被垃圾收集的时候,它持有的引用也同时被回收。
创建一个闭包仅仅为了修改自身的内部状态,不是值得提倡的闭包用法,更常见的用法是绑定常量或者不可变的值。
函数式语言向语言和运行时让渡控制权的途径——闭包——Java8之前的模拟
Counter
类还可以有别的一些写法(比如写成匿名的、泛型的,等等),但不管怎么做,都避免不了要自己去管理状态。闭包在这里表现出来的函数式思维就是“让运行时去管理状态”。比起自己硬着头皮去处理字段创建、呵护状态(包括经受多线程环境的严酷考验)这些繁琐的事务,还不如交出对状态的控制权,让语言和框架悄悄在背后帮我们管理好。
函数式语言向语言和运行时让渡控制权的途径——闭包——抓住上下文,而非状态
让语言去管理状态。
闭包还是推迟执行原则的绝佳样板。把代码绑定到闭包之后,可以推迟到适当的时机再执行闭包。这个特点在很多场合都能发挥作用。例如必要的变量和函数可能并不在定义时的作用域里,要到执行的时候才准备好。那么把执行上下文放在闭包里保留起来,就可以等到正确的时机再完成执行。
命令式语言围绕状态来建立编程模型,参数传递是其典型特征。闭包作为一种对行为的建模手段,让把代码和上下文同时封装在单一结构,也就是闭包本身里面,像传统数据结构一样可以传递到其他位置,然后在恰当的时间和地点完成执行。
抓住上下文,而非状态。