功能抽象是对可执行代码的抽象。
《SICP·第一章构造过程抽象》,看得我有些凌乱。这里重新组织一下Scheme功能抽象部分的知识。
行为抽象/功能抽象是对(从指令、操作符发展到)可执行代码的抽象。从C/C++/Java这条语言线看,按照行为抽象的逻辑发展[1],其包含:
- 表达式
- 3种流程结构
- 函数/子程序和结构化分解
- 接口与实现相分离原则
- 抽象方法——函数抽象的最高形式。
- 设计通用性的函数[2]
那么,Scheme如何进行功能抽象,以及它有什么特色呢?
从Scheme等函数式语言看,按照行为抽象/功能抽象的逻辑发展,它也包括三种流程结构、函数和结构化分解;但是动态类型语言天然地不遵循接口与实现相分离原则(因此也没有抽象函数),而在设计通用性的函数上,其高阶函数概念大放异彩。
[1]参考《编程导论(Java)·第三章功能抽象》
[2]在函数级别,行为抽象中还有一个十分重要内容:设计通用性的方法,请参考[2.2.1求和函数]。
1.computational process(计算过程)与procedure(函数)
本章的开场白,第一句话我就不喜欢。yqj2065没有搞懂它的第一个术语computational process是什么意思。
We are about to study the idea of a computational process. Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data. The evolution of a process is directed by a pattern of rules called a program. People create programs to direct processes.
‘[注:processes、procedures加上日常生活中常用的processes,看翻译版的时候,会看得头晕。所以yqj2065将procedures全部译为函数。对于子程序,在命令式编程的C语言中称为函数,而函数式编程的Scheme中偏偏称为procedures。]
1.2 Procedures and the Processes They Generate
或许这是Processes 的含义最接近清晰的地方。A procedure is a pattern for the local evolution of a computational process.
]
为什么选Lisp?...这些特征中最显著的一点是,对processes的Lisp描述(称为procedures/函数,能够作为Lisp数据来表示和操作。对于填平数据与函数的传统划分,方式为函数视为数据——函数是头等公民、高阶函数。
2.表达式和3种结构
Every powerful language has three mechanisms for :
•primitive expressions, 基本表达形式【这里不仅说 表达式,也包含基本函数】,语言设计者关心的要素,程序员只是被动地知道它们。如原子/基本类型、特殊块(特殊形式)define、if,基本操作符等。
•means of combination, 组合的手段。表达式定义中,组合表达式是基本要求。函数的组合则是函数式编程语言最典型的特征。数据类型的合成,也是编程语言的一个较重要的部分,只不过程序员自定义类型在C、Java中不那么引人注目。
•means of abstraction, 抽象的手段(命名与封装)给组合实体一个名字并作为操作的单元。这里,我们可以给常量、组合表达式、函数命名。
1.块(form)
Scheme中文字是基本表达式(primitive expression),而块(form)是基本程序单元,form 都由小括号括起来,如(+ 1 2)。一个Scheme语言程序是由一个或多个form构成。
1.1.1 用括号限定的一个表达式列表,用于表示函数应用的这些表达式,称为组合式。列表最左的那个元素称为操作符,其他为操作数——实参。
1.1.2
变量:using names to refer to computational objects。
Define 是该语言最简单的抽象手段。
用变量逐一为更复杂的表达式命名?a Lisp program usually consists of a large number of relatively simple procedures.【这句话容易导致误解。难怪有人说函数式编程是“自低而上”,从目前的最简单的例子出发,可以看到由简单的函数逐步构造出复杂的函数。另一方面,一个复杂的函数,需要分解为多个小函数,即功能分解——一个程序也是由简单的函数构成。函数式编程仍然可以结构化编程】
1.1.4 函数抽象(定义函数)
1.1.6 介绍分支结构
1.2 介绍递归Vs.迭代。这一节看得令我不爽。在解释器的尾调用优化 tail-call optimization (TCO)的支持下,一般递归和尾递归有了两种执行的方式。明明白白地写出来解释器的支持,很好理解。
3.1.1 介绍顺序结构begin。由于顺序结构必然产生副作用(除了最后的表达式作为整个form的值,其他的表达式就是用于产生副作用的),放在前面介绍一下也可以。
命令式编程Vs.函数式编程,从程序结构上看(表面上看),前者为顺序结构,后者为(solving <param list>).
3.函数抽象
由于函数抽象在define后介绍[1.1.4],而[1.3.2Constructing Procedures Using Lambda]中写“it seems terribly awkward to have to define trivial procedures”真是像讲故事。lambda作为匿名函数与命名函数各有用处。
4.接口与实现相分离
[1.1.8 Procedures as Black-Box Abstractions]介绍了这方面的内容。主要是Scheme在此没有太多手段,方法的形参是局部变量/约束变量也讲了1/3的篇幅;于是,define内部define既是隐藏helper函数的手段又是解决命名空间的手段。
5.高阶函数
[1.3 Formulating Abstractions with Higher-Order Procedures]是重点和Scheme的主场。功能抽象的一个重要方面:由函数到一般性函数。在Java中通常使用模板方法模式完成,(Java 8后,如果将它添加到功能抽象中加以讲解呢?)
其他:求值顺序