第十一章重构和测试函数式程序
本章介绍
■重构函数式程序
■使用不变性推理代码
■为 F# 程序写单元测试
■使用延迟值缓存结果
这本书的主题之一就是,函数编程理如何使解代码更容易理解,只需要通过阅读就可以;特别是在需要修改陌生程序,或者通过组合现有函数实现行为,或者重构现有的代码时,尤为重要。函数式编程更容易重构,缘于清晰度和模块化:可以改善代码,并且有信心这种改变不会破坏程序的其他部分。
正如在函数式编中的很多事情一样,修改代码而不改变其含义的思想,与数学密切相关,因为不改变表达式含义的操作是许多数学任务的基础。我们可以把一个复杂的等式,化简(simplify)得到更易于阅读、但意思相同的等式。我们看看下面的方程:y = 2x + 3(5 - x)。如果我们用3 乘以括号中的表达,可以写成y = 2x + 15 - 3x,又可以简化为:y = 15 – x。
来自数学的另一种技巧是替换(substitution)。如果有两个方程,y = x/2 和x = 2z,我们可以把第二个方程的右边代入第一个方程,就得到(化简后)y = z。最重要的一点是,把一个正确的方程替换成另一个方程,得到的方程不会突然变得不正确。这种方法在函数编程称为组合(composition)。
函数编程与是数学密切相关,所以,代数中的一些应用到函数式程序中,毫不奇怪。在编程的世界里,化简方程对应于重构(refactoring),这是本章的核心。特别是,我们将讨论减少重复代码和代码的依赖关系。
替换也是一种形式的重构,但是,我们将知道,它还有其他的重要优势,尤其是在单元测试当中。替换能够把精力集中在测试基本函数,花更少的时间测试,由简单的预制块组合的函数,因为组合不会破坏已测试的组件。
我们还将研究与重构密切相关的主题。如果程序没有副作用,其中个别部分不管搂什么顺序执行,应该得到同样的结果。值,只要声明,就可以计算,也可以延迟执行,直到真正需要这个值时。这种技术称为延迟laziness(或延迟计算lazy evaluation),当我们探讨潜在的无穷数据结构和计算机值的缓存时,将展示一些实际的好处。