# 读函数式编程思维
## 总的说下
这本书总得讲了下函数式思维,以及用各种语言来掩饰了下函数式思维。。。后面我用 swift 来演示吧
## 第一章 为什么要用函数式思维
函数式思维是编程范式的转换,最重要的是我们要正确的掌握语言给我们提供的特性,才能把一个语言用好
随着计算机性能提升以及编译器的不断优化,可以把一些控制权(比如内存管理,多线程等琐碎的语言细节)交给运行时来处理,用更好和更简洁的抽象来解决我们的问题
## 第二章 转变思维
命令式编程是按照“程序是一系列改变状态的命令”来建模的一种编程风格。
比如 `for` 循环处理数组,我们平时使用的时候,是通过确定初始状态,然后每次迭代都执行循环的命令,在循环的命令里面我们完成业务逻辑
函数式方法,讲程序描述为表达式和变换,以数学方程的形式建立模型,并尽可能避免可变状态
通过将数据集,进行 filter,map,reduce 等转换,得到我们想要的结果,以一种定义或是描述式的方式来处理
所有的函数式语言里面都会以不同的形式提供 filter(过滤),map(映射),reduce(折叠或者化约)
## 第三章 责权让渡
函数式思维的好处之一,是能够将低层次细节(如垃圾收集)的控制权移交给运行时,从 而消弭了一大批注定会发生的程序错误。开发者们可以一边熟视无睹地享受着最基本的抽 象,比如内存,一边却会对更高层次的抽象感觉突兀。然而不管层次高低,抽象的目的总 是一样的:让开发者从繁琐的运作细节里解脱出来,去解答问题中非重复性的那些方面。
1. 不再使用类似 `for`,`while` 之类的迭代,而是使用高阶函数,比如 map
2. 闭包,关注函数执行的上下文,而非通过闭包去控制状态
3. 柯里化,实现函数工厂,模板方法,隐藏参数
4. 递归,递归更加优雅,以及更容易让我们考虑无限的集合
5. Stream 和作业顺序重排
## 第四章 用巧不用蛮
记忆,和缓存类似,但是不需要我们来管理,只需要在函数调用后,告诉函数需要记忆此次调用的结果,下次使用相同参数调用的时候,就不需要再次进行计算了
swift 中的参考 [memoize](https://gist.github.com/berkus/8a9e104f8aac5d025eb5#file-memoize-swift)
缓求值,不到逼不得已,不去对函数进行求值。
缓求值可以处理无限长的序列,减少存储空间占用,缓求值还有利于运行时产生高效代码
## 第五章 演化的语言
在面向对象的世界里,我们针对具体问题,建立专门的数据结构,以方法的形式将专门的操作关联在数据结构上。
函数式编程,只使用很少的一组关键数据结构(list,set,map)搭配专为这些数据结构深度优化过的操作。
比起在定制的类结构上做文章,把封装的单元缩小到函数级别,有利于在更基础的层面上 更细粒度地实施重用。
Clojure 很好地发挥了这方面的优势,例如在 XML 的解析问题上。 Java 语言的 XML 解析框架数量繁多,每一种都有自己的定制数据结构和方法语义(如 SAX 和 DOM 都是自成一体)。Clojure 的做法相反 ,它不鼓励使用专门的数据结构,而是 将 XML 解析成标准的 Map 结构。而 Clojure 有极为丰富的工具可以与 map 结构相配合
使用模式匹配来替代长长的 if
要想契合问题域的表达习惯,可以利用运算符重载来改变语言的外貌,不必 创造全新的语言。
函数式数据结构,使用 Either 表示两种结果的返回值,使用 Option 来表示有为空返回值的类型
## 第六章 模式与重用
函数式语言有函数式语言的设计模式,传统的对于函数式语言来说,因为语言的特性,让部分模式变得没有意义了,但是部分问题还是存在,他们在函数式的世界里面,通过其他的方法来解决了
传统设计模式在函数式编程的世界中大致有三种归宿。
- 模式已被吸收成为语言的一部分。
- 模式中描述的解决办法在函数式范式下依然成立,但实现细节有所变化。
- 由于在新的语言或范式下获得了原本没有的能力,产生了新的解决方案(例如很多问题都可以用元编程干净利落地解决,但 Java 没有元编程能力可用)。
函数级别的重用
因为函数式编程的特点,重用的最小单位变成了函数,并且程序由多个函数组合而成。
Template Model,通过使用函数变量,进行了化简,减少耦合
Strategy 模式,更加灵活
Flyweight 模式,使用记忆来实现
Factory 模式和柯里化,柯里化就是产出函数的工厂
以结构为载体的代码重构需要考虑整个类的关系网,可以很好的减少耦合的情况。
## 第七章 现实应用
Java 8 中的函数式接口,Option 类型,Stream,都是函数式语言在 Java 语言中的应用
函数试的构架,贯彻“值不可变”,学习从值不可变的角度去思考
可变的状态与测试数量有直接的关联:可变的状态越多,要求的测试也越多。
实现一个值不可变的 Java 类,我们需要做到以下事情。
- 把所有的字段都标记为final。Java 要求被标记为 final 的字段,要么在声明时初始化,要么在构造器中初始化。不要 在意 IDE 大惊小怪地提醒我们字段没有在声明位置上初始化,当我们在构造器里写好 相关的初始化代码,IDE 就会明白过来。
- 把类标记为final,防止被子类覆盖。 如果类可以被覆盖,类中的方法也就有可能被改变行为,因此以防万一,我们干脆禁止 子类化。Java 的 String 类就采取了这样的防范策略。
- 不要提供无参数的构造器。 一个值不可变的对象,它的一切状态都必须通过构造器来设定。假如我们没有需要设定 的状态,那建立这么一个对象又有何必要呢?在无状态的类里面安排几个静态方法就足 够了。所以说,值不可变的类根本不应该出现无参数的构造器。假如我们受到某些框架 的限制,不得不提供无参数的构造器,这时可以考虑能否用一个私有的无参数构造器来 满足框架的要求(私有的构造器仍然可以通过反射来访问)。 JavaBeans 的标准规定要有默认构造器,我们摒除无参数构造器违反了这条规定。不过 反正 JavaBeans 里有各种 setXXX 方法存在,本身就不可能是值不可变的。
- 提供至少一个构造器。 构造器是我们在对象里添置状态的最后机会!
- 除了构造器之外,不要提供任何制造变化的方法。我们不但要避免沿袭 JavaBeans 风格的 setXXX 方法,还必须小心防范,不能返回任何 值可变的对象引用。标记了 final 的对象引用并不等于它所指向的一切都不可改变。因 此,我们需要预防性地复制所有通过 getXXX 方法返回的对象引用。
对于 web 框架,函数式语言回很合适,因为整个 web 就是一系列从请求到响应的变换