编程范式三
编程的本质
- Programs = Algorithms + Data Structures
- Algorithm = Logic + Control
第一个表达式倾向于数据结构和算法,如果数据结构设计得好,算法也会变得简单,而且一个好的通用的算法应该可以用在不同的数据结构上。
第二个表达式则想表达的是数据结构不复杂,复杂的是算法,也就是我们的业务逻辑是复杂的。我们的算法由两个逻辑组成,一个是真正的业务逻辑,另外一种是控制逻辑。程序中有两种代码,一种是业务逻辑代码,另一种是控制我们程序的代码,叫控制代码,业务逻辑不关心这个事情。
算法的效率往往可以通过提高控制部分的效率来实现,而无须改变逻辑部分,也就无须改变算法的意义。控制部分用来描述如何使用逻辑,最粗略的看法可以认为“控制”是解决问题的策略,而不会改变算法的结果,因为算法的结果是由逻辑决定的。对同一个逻辑,使用不同控制,所得到的算法,本质是等价的,因为它们解决同样的问题,并得到同样的结果。
总之,通过这两个表达式,我们可以得出:
- Program = Logic + Control + Data Structure
各种不同的编程范式,或是程序设计的方法,其实,都是在围绕着这三件事来展开。比如:
- 函数式编程中的 Map/Reduce/Filter,它们都是一种控制。而传给这些控制模块的那个 Lambda 表达式才是我们要解决的问题的逻辑,它们共同组成了一个算法。最后,我再把数据放在数据结构里进行处理,最终就成为了我们的程序。
- 面向对象中依赖于接口而不是实现,接口是对逻辑的抽象,真正的逻辑放在不同的实现类中,通过多态或是依赖注入这样的控制来完成对数据在不同情况下的不同处理。
编程的本质问题就是:有效地分离 Logic、Control 和 Data 是写出好程序的关键所在。
- Control 可以标准化的。比如:遍历数据、查找数据、多线程、并发、异步等,都是可以标准化的。
- Control 需要处理数据,标准化 Control,需要标准化 Data Structure,可以通过泛型编程来解决这个事。
- Control 还要处理用户的业务逻辑,即 Logic。所以,可以通过标准化接口 / 协议来实现,确保 Control 模式可以适配于任何的 Logic。
Logic是程序复杂度的下限,然后,我们为了控制程序,需要再搞出很多控制代码,于是 Logic+Control 的相互交织成为了最终的程序复杂度。
逻辑编程范式
Prolog(Programming in Logic)是一种逻辑编程语言,它创建在逻辑学的理论基础之上,最初被运用于自然语言等研究领域。现在它已被广泛地应用在人工智能的研究中,可以用来建造专家系统、自然语言理解、智能知识库等。
Prolog 语言最早由艾克斯马赛大学(Aix-Marseille University)的 Alain Colmerauer 与 Philippe Roussel 等人于 20 世纪 60 年代末研究开发的。1972 年被公认为是 Prolog 语言正式诞生的年份,自 1972 年以后,分支出多种 Prolog 的方言。
有别于一般的函数式语言,Prolog 的程序是基于谓词逻辑的理论。最基本的写法是描述对象与对象之间的关系,之后可以用询问目标的方式来查询各种对象之间的关系。系统会自动进行匹配及回溯,找出所询问的答案。
我们再来看一个经典的四色地图问题。任何一个地图,相邻区域不能用相同颜色,只要用四种不同的颜色就够了。
首先,定义四种颜色。
color(red).
color(green).
color(blue).
color(yellow).
然后,定义一个规则:相邻的两个地区不能用相同的颜色。
neighbor(StateAColor, StateBColor) :- color(StateAColor), color(StateBColor),
StateAColor \= StateBColor. /* \= is the not equal operator */
接着,我们描述事实就好了,描述哪些区域是相邻的事实。
germany(SH, MV, HH, HB, NI, ST, BE, BB, SN, NW, HE, TH, RP, SL, BW, BY) :-
neighbor(SH, NI), neighbor(SH, HH), neighbor(SH, MV),
neighbor(HH, NI),
neighbor(MV, NI), neighbor(MV, BB),
neighbor(NI, HB), neighbor(NI, BB), neighbor(NI, ST), neighbor(NI, TH),
neighbor(NI, HE), neighbor(NI, NW),
neighbor(ST, BB), neighbor(ST, SN), neighbor(ST, TH),
neighbor(BB, BE), neighbor(BB, SN),
neighbor(NW, HE), neighbor(NW, RP),
neighbor(SN, TH), neighbor(SN, BY),
neighbor(RP, SL), neighbor(RP, HE), neighbor(RP, BW),
neighbor(HE, BW), neighbor(HE, TH), neighbor(HE, BY),
neighbor(TH, BY),
neighbor(BW, BY).
最后,我们执行如下语句,就可以让 Prolog 推导 各个区域 的颜色。
?- germany(SH, MV, HH, HB, NI, ST, BE, BB, SN, NW, HE, TH, RP, SL, BW, BY).
Prolog 这种逻辑编程,把业务逻辑或是说算法抽象成只关心规则、事实和问题的推导这样的标准方式,不需要关心程序控制,也不需要关心具体的实现算法。只需要给出可以用于推导的规则和相关的事实,问题就可以被通过逻辑推导来解决掉。
程序世界里的编程范式
这世界上纷乱的编程范式,可以分成这几类:声明式、命令式、逻辑的、函数式、面向对象的、面向过程的。
- 中间两个声明式编程范式(函数式和逻辑式)偏向于你定义要什么,而不是怎么做。
- 而两边的命令式编程范式和面向对象编程范式,偏向于怎么做,而不是要做什么。
基本上来说,就是两大分支,一边是在解决数据和算法,一边是在解决逻辑和控制。
程序编程范式。一个是左脑,一个是右脑。我们程序员基本上是在用左脑,左脑是理性分析,喜欢数据证据,线性思维,陷入细节,具体化的,不抽象。但是,实际上玩儿出这些东西的都在右脑,函数式,还有像逻辑式的抽象能力都在右脑。所以我们非线性的想象力都在这边,而标准化教育把我们这边已经全部干掉了,我们只剩左边。我们陷入细节,我一说 Java 是最好的程序设计语言,一堆人就来了,找各种各样的细节问题跟你纠缠。