# 重要范式
# 命令范式
imperative programming ——一切行动听指挥
- 世界观
程序是由若干行动指令组成的有序列表
- 方法论
用变量来存储数据,用语句来执行指令
- 程序特点
命令式编程的程序由命令序列组成,即一系列祈使句:‘先做这,再做那。’,强调‘怎么做’。
更学术点说,是冯诺依曼机运行机制的抽象,即依序从内存中获取指令和数据,然后去执行。
- 过程式编程(procedural programming)
是指引入了过程、函数、子程序的命令式编程。
- 结构化编程(structured programming)
是在过程式编程的基础上发展起来的。
其本质是一种编程原则,提倡代码的逻辑应具有清晰的逻辑结构,以保证程序易于读写、测试、维护和优化。
结构化编程即使在三种基本结构(顺序、选择、循环)的基础上进行嵌套组合
思想包含两方面:
在微观上,主张循规守矩,采用顺序、选择和循环3中逻辑结构,摒弃和限制goto语句,以避免杂乱无章的代码。
在宏观上,主张分而治之(divide and conquer),采用‘自顶向下(top-down)’的设计,
通过模块化将一个较为复杂的系统分解为若干相对简单的子系统,
每个子系统可以独立的进一步分解,知道容易编码实现为止。
这两方面是互为因果、互为保障的——由基本结构拼装而成的系统一定是模块清晰、层次分明的;
反之,系统逐步分解到最后,一定会演化成基本结构。
# 声明范式
declarative programming —— 目标决定行动
- 背景
声明式编程发轫于人工智能的研究
- 特点
声明式编程由若干规范的声明组成,
即一系列的陈述句:‘已知这,求解那’,强调‘做什么’而非‘怎么做’。
声明式编程是人脑思维方式的抽象,即利用数理逻辑或既定范式对已知条件进行推理和运算。
让我们重回数学思维
变量也如数学中的一样,是抽象符号而非内存地址
没有复制运算,不会产生变量被改写的副作用,也不存在内存分配和释放的问题
简化了代码,也减少了调试
- 分类
- 逻辑式编程(logic programming, LP)
类似逻辑推理
- 函数式编程(functional programming, FP)
类似代数中的表达式变换和计算
- 其他声明式编程
- 属性导向式编程(attribute-oriented programming)
- 数据流式语言(dataflow programming)
- 约束式
- 规范式
- 标记式
- 与命令范式
声明式语言和命令式语言并非泾渭分明,而是相互交叉渗透的。
总的来说,在命令式语言中融入声明式的元素应当是一种趋势,尤其是函数式。
- 本质
命令式编程是行动导向的(Action-Oriented),因而算法是显性而目标是隐性的
声明式编程是目标驱动的(Goal-Driven),因而目标是显性而算法是隐性的
- 语言的发展
声明式编程重目标、轻过程,专注问题的分析和表达而不致陷入算法的迷宫,其代码也更加简洁清晰、易于修改和维护。
从这种意义上说,声明式语言天然地就比命令式语言更高级。
计算机语言发展的前3代基本上都是命令式的,而后两代基本都是声明式的,由此可见一斑。
(命令式是模拟电脑的,声明式是模拟人的)
- 应用场景
声明式语言——尤其是函数式和逻辑式,擅长基于数理逻辑的应用,如人工智能、符号处理、数据库、编译器等
对基于业务逻辑的,尤其是交互式或事件驱动型的应用就不那么得心应手了。
而大多数软件是面向用户的,交互性强、多为事件驱动、业务逻辑千差万别,显然命令式语言在此更有用武之地。
- 迭代与递归
命令式语言提倡迭代不鼓励递归。
因为迭代比递归更符合命令式思维,前者贴近机器语言,后者更贴近数学语言。
除尾递归(tail recursion)外,一般递归比迭代的开销(overhead)大。
声明式语言则相反,提倡递归不支持迭代。
就语法而言,它不允许迭代中的循环变量;就视角而言,迭代着眼微观过程而递归着眼宏观规律。
- 编程的解决方案
任何语言都难脱命令式和声明式的窠臼(kejiu)。事实上,范式非命令式的编程都可以归为声明式的。
因此命令式、函数式和逻辑式 是最核心的三种范式。
命令式把程序看做一个自动机,输入是初始状态,输出是最终状态,编程就是设计一系列的指令,通过自动机执行以完成状态转变;
函数式把程序看做一个数学函数,输入是自变量,输出是因变量,编程就是设计一系列的函数,通过表达式变换以完成计算;
逻辑式把程序看做是一个逻辑证明,输入是题设,输出是结论,编程就是设计一些列的命题,通过逻辑推理以完成证明
# 对象范式
object-oriented programming)——民主制社会的编程法则
- 核心思想
可泛化为:以数据为中心组织逻辑,将系统视为相互作用的对象集合,并利用继承和多态来增强可维护性、可扩展性和可重用性。
这种思想也能应用到函数式和逻辑式中,只不过对象的方法从命令式中的过程分别换成函数式中的函数和逻辑式中的断言罢了。
- 优势
OOP适用于大型复杂的、交互式的、尤其是与现实世界密切相关的系统,但在小型应用、数学计算、符号处理等方面并无优势。
- 比较
大致上说,命令式、函数式和逻辑式互相平行,而OOP与它们正交
- 编程理念
过程式编程的理念是以过程为中心的,自上而下、逐步求精。
OOP则正好相反,以数据为中心,自底而上、逐步合并。
过程式编程的模块以函数为单位,OOP的模块以对象为单位。
- OOP 人性化表现
对象是现实中具体事物和抽象概念的模拟,使OOP更接近人类的认知模式。
接口简洁易记。说白了,OOP就是将相关的函数用数据粘合,重新包装后再贴上对象的标签。
从这个角度看,与其说OOP更具重用性,不如说更具易用性。
# 并发范式
(concurrent programming)——合作与竞争
- 思想
并发式编程以进程为导向(Process-Oriented)、以任务为中心将系统模块化。
并发式编程以资源共享和竞争为主线。
这意味着程序设计将围绕进程的划分和调度、进程之间的通信和同步等来展开
- 设计考量
软件易于重用、维护、测试;
公平有效地利用资源,优化程序性能,如增大吞吐量、减少响应时间、提高效率等;
保障进程安全、防止竟态条件(Race Condition);
保持进程活性,便面死锁、饥饿、活锁、资源枯竭等;
减少锁开销、上下文切换等带来的性能损耗;
妥善处理多进程在算法、调试等方面带来的复杂性。
- 实现
声明式语言具有无副作用的特性,尤其适合并发式编程。
目前,究竟是在语言级别上支持并发,还是交由操作系统处理的问题上,仁者见仁,智者见智。
Java和C#等选择前者,在语法上对并发编程有一定的支持。
C和C++等则选择后者,除了关键字volatile外,主要靠库函数支持
Erlang 支持函数式和并发式,采用消息传递的并发模型
- 并发式和对象式
- 相通之处
他们均与三大核心范式正交,并且越来越广泛地向他们渗透
均为传统编程的一种推广——
并发式进程个数为1时,即为传统的串行式编程,对象的方法个数为0即为传统的数据类型;
均将整个程序系统分解为若干独立的子系统,不同的是一个以任务为单位,一个以对象为单位;
子系统之间均能交流和合作,不同的是一个以竞争为主题,一个以服务为主题
- 小结
如果将程序系统视为公司,那么并发式系统是产品型公司,每个进程是一名员工,其职责是执行单一任务;
对象式系统是服务型公司,每个对象时一名服务员,其职责是提供系列服务。
由此可见,一名优秀的程序设计师也应该是一名优秀的管理者。
# 总结
- 地位
最传统 1个 命令式
最基本 2个 命令式和声明式
最核心 3个 命令式、函数式和逻辑式
最主要 4个 命令式、函数式、逻辑式、对象式
最重要 5个 命令式、函数式、逻辑式、对象式、并发式
- 思想
过程式:以过程为模块的君主体系,模块之间互相授命和听命。 —— 机器风格
函数式:以函数为模块的数学体系,模块之间互相替换和合成。 —— 数学风格
逻辑式:以断言为模块的逻辑体系,模块之间互相归纳和演绎。
对象式:以对象为模块的民主体系,模块之间互相交流和服务。 —— 现实风格
并发式:以进程为模块的生成体系,模块之间互相竞争和合作。