左耳听风_039_38_编程范式游记9-_编程的本质
你好,我是陈浩网名应做耳朵house.在前面呢我们讲了各式各样不同语言的编程范式,从c语言的泛型啊讲到c加加的泛型,再讲到函数式的map reduce和filter啊,以及pipeline和decorator,还有面向对象的多态。
通过依赖接口而不是实现的桥节模式、策略模式和调理模式啊,以及面向对象的LC,还有javascriript原型编程在运行时啊对对象原型进行修改,还有购物员的委托模式等等。
那所有的这一切呢,不知道你是否看出了一些端倪啊,或者是其中的一些共性呢?我先来介绍两篇论文。
在一九七六年啊,一位来自瑞士的计算机科学家algol语言、modular语言、orberon语言和pasaska语言的设计师nickloss ML worth啊,写了一本非常经典的书,叫做orgorithms plus data structures equals programs.那翻译成中文呢就是算法加上数据结构等于程序。
那这本书呢主要写了算法和数据结构的关系,它对计算机科学啊,尤其是对计算机科学的教育影响非常深远。
在一九七九年呢,一位来自英国的逻辑学家和计算机科学家robert cwaski啊发表了一篇论文,叫做algorithms equislogic plus control.那robert cowaskia从二十世纪七十年代末到整个八十年代,致力于对数据库的研究,并在计算机证明、数学定理等等当年重要的应用上颇有建树。
啊,尤其是在逻辑控制和算法等方面提出了革命性的理论,大大的影响了数据库编程语言。
啊,直到今天的人工智能,那robert coski在这篇论文里提到任何算法呢都会有两个部分,那一个呢是logic部分,那这个呢是用来解决实际问题的。
另一个呢是control部分。
那这个呢是用来决定用什么策略来解决问题。
那捞这个部分啊是真正意义上解决问题的算法,而control部分呢是影响解决这个问题的效率。
那程序运行的效率问题和程序的逻辑其实是没有关系的。
我们认为啊如果将logic和control部分有效的分离开,那么代码呢就会变得更容易改进和维护。
注意最后一句话是重点。
那这两位老先生呢提出了两个表达式,那第一个呢是programs,等于algorithms,加上data structures.那第二个呢是hhorithms,等于logic加上control.那第一个表达式啊倾向于树结构和算法,他是想把这两个拆分开,那早期呢都在走这条路。
那他们认为啊,如果数据结构设计的好,算法也会变得简单,而且一个好的通用的算法应该可以用在不同的数据结构上。
而第二个表达式想表达的是数据结构不复杂而复杂的是算法啊,也就是我们的业务逻辑是复杂的。
我们的算法有两个逻辑组成,一个呢是真正的业务逻辑。
那另外一种呢是控制逻辑,所以程序中呢有两种代码,那一种呢是真正的业务逻辑代码。
那另一种代码呢是控制我们程序的代码啊,也叫控制代码。
而这个呢根本不是业务逻辑,业务逻辑啊不关心这个事情。
那算法的效率呢往往可以通过提高控制部分的效率来实现,而无需改变逻辑部分,也就无需改变算法的意义了。
我举个阶乘的例子,n的阶乘呢等于n乘以n减一,乘以n减二乘以n减三啊,一直乘到一。
那逻辑部分呢用来定义阶乘,首先呢一是零的阶乘。
那其次呢,如果v是x的阶乘,并且u等于v乘以x加一,那么u呢就是x加一的阶乘。
那用这个定义呢,我既可以从上往下的把x加一的阶乘缩小为先,计算x的阶乘,再将结果乘以x加一,也可以由下而上,逐个计算一系列阶乘的结果。
控制部分呢是用来描述如何使用逻辑的那最粗略的看法可以认为控制是解决问题的策略,而不会改变算法的意义。
因为算法的意义是由逻辑决定的那对同一个逻辑使用不同的控制,所得到的算法呢本质上是等价的。
因为他们解决同样的问题啊,并且得到同样的结果。
所以呢我们可以通过逻辑分析来提高算法的效率,保持它的逻辑,而更好的使用这一逻辑。
比如说有时候呢我用自上而下的控制替代自下而上啊,这样能提高效率。
那如果把自上而下的顺序执行改成并行执行呢,也会提高效率。
总之呢通过这两个表达式,我们可以得出program,等于logic加上control,加上data structure.那前面讲了这么多的编程范式或者是程序设计的方法,那其实呢我们都是在围绕这三件事儿来做的。
比如就像函数式编程map, reduce ed filter,它们呢都是一种控制。
而传给这些控制模块的那个兰姆达表达式啊,才是我们要解决的问题的逻辑,它们共同组成了一个算法。
那最后呢我再把数据放到数据结构里进行处理,最终呢就成为了我们的程序。
再比如我们之前购员委托模式那个undo的例子,那俺do这个事儿呢是我们想要解决的问题是logic,但是俺do的流程呢是控制。
还有呢我们要在对象中要依赖于接口,而不是实现。
那接口呢是对逻辑的抽象。
真正的逻辑呢放在不同的具体实现类中,通过多态或者依赖注入这样的控制,来完成对数据在不同情况下的不同处理。
那如果你再仔细的结合我们之前讲的各式各样的编程范式来思考前面的这些概念的话,你是不是会觉得所有的语言或者编程范式都在解决上面的这些问题呢?啊,也就是这么几个事儿。
那第一呢control是可以标准化的,比如遍历数据,查找数据、多线程并发,还有异步都是可以标准化的那第二呢,因为control需要处理数据,所以标准化control呢需要标准化,data structure,我们可以通过泛型编程来解决这个事儿。
那第三呢,control还要处理用户的业务逻辑啊,也就是logic.所以呢我们可以通过标准化接口和协议来实现让我们的control模式啊可以适配于任何的logic.那这三点呢就是编程范式的本质,你一定要明白啊,有效的分离logic control和data是写出好程序的关键所在。
我们在写代码的时候呢,会看到好多把控制逻辑和业务逻辑放到一块的啊,这种代码里面有一些变量和流程,是跟业务相关的那有一些呢是不相关的业务逻辑决定了程序的复杂度。
所以如果业务逻辑本身就复杂,你的代码就不可能写的简单。
但是业务逻辑logic是程序复杂度的下限,然后呢我们为了控制程序,需要再搞出很多控制代码,于是捞这个加上CTRL相互交织,就成了最终的程序复杂度。
那逻辑和控制混淆之后会是什么结果呢?我们来看一个例子,这是我在扣上做的一道题啊,作用是通配符匹配,给两个字符串做匹配。
你可以到文中啊看一下我写出来的这个代码,我也不知道我怎么写出来的啊,好像是为了要通过,我需要关注于性能。
你看上面这段代码有多乱,如果我不写注释呢,你可能都看不懂了,就算我写了注释以后你敢改吗?你可能连动都不敢动,这些代码里面啊很多都不是业务逻辑,是用来控制程序的逻辑。
那业务逻辑是相对复杂的,但是控制逻辑和业务逻辑交叉在一块儿。
虽然代码写的不多,但是这个代码已经够复杂了。
过两三天以后呢,我回头看我到底写的什么,我也不懂为什么会写成这样呢?我当时脑子啊是怎么想的,我完全不知道我现在啊就是这种感觉。
那么怎么把这段代码写的更好一些呢?首先呢我们需要一个比较通用的状态机来维护匹配的开始和结束的状态。
那这个呢属于control.那如果我们做的好的话,还可以抽象出一个像程序的文法分析一样的东西。
那这个呢也是control.然后呢我们把匹配星号和问号的算法形成不同的匹配策略。
那这样呢我们的代码就会变得漂亮一些了。
而且啊也会快速一些。
那这里有一篇正则表达式高效算法的论文,regular expression matchine can be simple and fast.啊,推荐你点开文中的链接,读一读,里面有相关的实现啊,我在这里呢就不多说了。
在这里呢我想说的是,程序的本质是logic,加上control加上data,而其中呢logic和control是关键。
我们要注意这个和系统架构啊也有相通的地方。
逻辑呢是你的业务逻辑逻辑过程的抽象,再加上一个由术语表示的术语结构的定义。
控制逻辑啊是跟你的业务逻辑是没关系的。
你控制他执行,控制一个程序流转的方式呢,也就是程序执行的方式并行,还是串行同步还是异步。
还有调度不同执行路径或者模块,还有数据之间的存储关系。
那这些和业务逻辑啊都没有关系。
那如果你看过那些混乱不堪的代码,你会发现其中最大的问题呢就是我们把logic和control啊纠缠在一起了。
所以呢就会导致代码很混乱,难以维护。
而且呢bug很多。
绝大多数程序复杂的原因呢就是这个问题啊,就像文中这张图表现的情况一样。
我们再来看一个简单的例子,那文中贴出来的呢是一段检查用户表单信息的常见代码啊,我相信这样的代码你见的多了。
但其实呢我们可以做一个DSL,再加上DSL的解析器啊,就像这样DSL的描述是logic,而我们的check form呢则成了control.那这样写出来的代码就非常好看了。
好了,这节课到这里呢就结束了。
我们来简单总结一下,关于代码的复杂度呢,我们要记住三点。
那第一呢业务逻辑的复杂度决定了代码的复杂度。
那第二呢,控制逻辑的复杂度加上业务逻辑的复杂度啊,会导致程序代码混乱不堪。
那第三呢,绝大多数程序复杂混乱的根本原因就是业务逻辑啊和控制逻辑相耦合了。
那如何分离control和logic呢?啊,我们可以使用以下这些技术来解耦,包括状态机DSL,还有之前提过的一些编程范式等等。
我的文中呢给你总结了具体的技术,那这里呢我就不再多说了。
那所谓编程的本质总结起来呢,就是捞这个部分才是真正有意义的。
而control部分呢只影响捞这个部分的效率。
那文末呢是编程范式游记系列文章的目录,方便你了解这一系列内容的全貌。