1.观点1.1.抽象隐藏了繁杂的细节,只是有时候会连同重要的考虑因素一起隐藏掉1.2.理解掌握的抽象层次永远要比日常使用的抽象层次更深一层1.3.交出控制权的观点:放弃对繁琐细节的掌控,关注问题域,而非关注问题域的实现2.函数式思维的好处2.1.将低层次细节(如垃圾收集)的控制权移交给运行时,从而消弭了一大批注定会发生的程序错误2.2.函数式语言的简洁语法和灵活配合,才使递归成为简单可行的代码重用选项之一2.3.运行时有能力在涉及效率的问题上替我们做决定2.4.从频繁出现的场景中消灭掉烦人的实现细节3.闭包(closure)3.1.一种特殊的函数,在生成的时候,会把引用的变量全部圈到代码块的作用域里,封闭、包围起来(故名闭包)3.1.1.闭包作为一种对行为的建模手段,让我们把代码和上下文同时封装在单一结构,也就是闭包本身里面,像传统数据结构一样可以传递到其他位置,然后在恰当的时间和地点完成执行3.2.闭包的每个实例都保有自己的一份变量取值,包括私有变量也是如此3.2.1.代码块实例从它被创建的一刻起,就持有其作用域内一切事物的封闭副本3.3.在缺乏闭包特性的旧版Java平台上,Functional Java利用匿名内部类来模仿“真正的”闭包的某些行为,但语言的先天不足导致这种模仿是不彻底的3.4.当作一种异地执行的机制,用来传递待执行的变换代码3.5.是推迟执行原则的绝佳样板3.6.抓住上下文,而非状态3.6.1.“让运行时去管理状态”4.柯里化(currying)和函数的部分施用(partial application)4.1.向一部分参数代入一个或多个默认值的办法来实现的4.1.1.这部分参数被称为“固定参数”4.2.柯里化4.2.1.从一个多参数函数变成一连串单参数函数的变换4.2.2.结果是返回链条中的下一个函数4.3.部分施用4.3.1.通过提前代入一部分参数值,使一个多参数函数得以省略部分参数,从而转化为一个参数数目较少的函数4.3.2.把参数的取值绑定到用户在操作中提供的具体值上,因而产生一个“元数”(参数的数目)较少的函数4.4.Groovy4.4.1.curry()函数实现柯里化4.5.Clojure4.5.1.(partial f a1 a2 …)函数4.5.2.没有将柯里化实现成一种语言特性,相关的场景交由部分施用去处理4.6.Scala4.6.1.柯里化4.6.2.部分施用函数4.6.3.偏函数4.6.3.1.PartialFunction trait是为了密切配合语言中的模式匹配特性4.6.3.2.trait并不生成部分施用函数。它的真正用途是描述只对定义域中一部分取值或类型有意义的函数4.6.3.3.Case语句是偏函数的一种用法4.6.3.4.偏函数的参数被限定了取值范围4.6.3.5.可以把偏函数用在任何类型上,包括Any4.7.大多数函数式语言都具备柯里化和部分施用这两种特性,但实现上各有各的做法4.8.用途4.8.1.函数工厂4.8.1.1.工厂方法的场合,正适合柯里化(以及部分施用)表现它的才干4.8.2.Template Method(模板方法)模式4.8.2.1.在固定的算法框架内部安排一些抽象方法,为后续的具体实现保留一部分灵活性4.8.3.隐含参数5.递归5.1.以一种自相似的方式来重复事物的过程5.2.对一个不断变短的列表反复地做同一件事,把递归用在这样的场合,写出来的代码就容易理解5.3.递归操作往往受制平台而存在一些固有的技术限制,因此这种技法绝非万灵药5.4.但对于长度不大的列表来说,递归操作是安全的5.5.语言在管理返回值,它从递归栈里收集每次方法调用的返回结果,构造出最终的返回值5.6.利用递归,把状态的管理责任推给运行时6.记忆(memoization)6.1.用更多的内存(我们一般不缺内存)去换取长期来说更高的效率6.1.1.缓存可以提高性能,但缓存有代价:它提高了代码的非本质复杂性和维护负担6.1.2.负责编写缓存代码的开发者不仅要顾及代码的正确性,连它的执行环境也要考虑在内6.1.3.代码中的状态,开发者不仅要费心照应它,还要条分缕析它的一切明暗牵连6.2.记忆的内容应该是值不可变的6.3.保证所有被记忆的函数6.3.1.没有副作用6.3.2.不依赖任何外部信息6.4.只有纯(pure)函数才可以适用缓存技术6.4.1.纯函数是没有副作用的函数6.4.1.1.它不引用其他值可变的类字段6.4.1.2.除返回值之外不设置其他的变量6.4.1.3.其结果完全由输入参数决定6.4.2.只有在函数对同样一组参数总是返回相同结果的前提下,我们才可以放心地使用缓存起来的结果6.5.缓存是很常见的一种需求,同时也是制造隐晦错误的源头6.6.两种情况6.6.1.类内部缓存6.6.1.1.类中的缓存就代表类有了状态,所有与缓存打交道的方法都不可以是静态的,于是产生了更多的连锁效应6.6.2.外部调用6.7.两种实现方式6.7.1.手工进行状态管理6.7.2.采用记忆机制6.8.在命令式的思路下,开发者是代码的主人(以及一切责任的承担者)6.9.我们写出来的缓存绝不可能比语言设计者产生的更高效,因为语言设计者可以无视他们给语言定的规矩:开发者无法触碰的底层设施,不过是语言设计者手中的玩物,他们拥有的优化手段和空间是“凡人”无法企及的6.9.1.上帝视角6.10.Groovy6.10.1.先将要记忆的函数定义成闭包,然后对该闭包执行memoize()方法来获得一个新函数,以后我们调用这个新函数的时候,其结果就会被缓存起来6.10.2.memoizeAtMost(1000)6.11.Clojure6.11.1.(memoize )6.12.Scala6.12.1.没有直接提供记忆机制,但它为集合提供的getOrElseUpdate()方法已经替我们承担了大部分的实现工作6.13.Java 86.13.1.没有直接提供记忆特性,但只要借助它新增的lambda特性,就可以轻松地实现记忆功能7.缓求值(lazy evaluation)7.1.尽可能地推迟求解表达式7.1.1.昂贵的运算只有到了绝对必要的时候才执行7.1.2.可以建立无限大的集合,只要一直接到请求,就一直送出元素7.1.3.按缓求值的方式来使用映射、筛选等函数式概念,可以产生更高效的代码7.1.4.减少占用的存储空间。假如能够用推导的方法得到后续的值,那就不必预先存储完整的列表了——这是牺牲速度来换取存储空间的做法7.2.非严格求值(non-strict)的(也叫缓求值,lazy)7.2.1.常用的非严格求值语言有Haskell7.3.Totally Lazy框架(Java)7.4.Groovy7.4.1.缓求值列表是函数式语言普遍具备的特性7.4.1.1.LazyList7.4.2.暂缓初始化昂贵的资源,除非到了绝对必要的时候7.4.3.可以用来构建无限序列,也就是没有上边界的列表7.4.4.缓求值列表特别适用于资源的生产成本较高的情况7.5.Clojure7.5.1.数据结构都是默认缓求值的7.6.Scala7.6.1.没有把一切都默认安排成缓求值的,而是在集合之上另外提供了一层缓求值的视图7.7.缓求值的字段初始化7.7.1.Scala7.7.1.1.val声明前面加上“lazy”字样7.7.1.1.1.令字段从严格求值变成按需要求值7.7.2.Groovy7.7.2.1.抽象语法树(Abstract Syntax Tree,AST)7.7.2.1.1.@Lazy标注8.元函数技法8.1.操纵的对象是函数本身,而非函数的结果8.2.柯里化