声明式编程和命令式编程(转载)
原文地址:声明式编程和命令式编程的比较
先统一一下概念,我们有两种编程方式:命令式和声明式。
我们可以像下面这样定义它们之间的不同:
- 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
- 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
声明式编程和命令式编程的代码例子
举个简单的例子,假设我们想让一个数组里的数值翻倍。
我们用命令式编程风格实现,像下面这样:
1 | var numbers = [1,2,3,4,5] |
我们直接遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。
而使用声明式编程方法,我们可以用 Array.map 函数,像下面这样:
1 | var numbers = [1,2,3,4,5] |
map 利用当前的数组创建了一个新数组,新数组里的每个元素都是经过了传入map的函数(这里是function(n) { return n*2 })的处理。
map函数所作的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传入map的是一个纯函数;它不具有任何副作用(不会改变外部状态),它只是接收一个数字,返回乘以二后的值。
在一些具有函数式编程特征的语言里,对于list数据类型的操作,还有一些其他常用的声明式的函数方法。例如,求一个list里所有值的和,命令式编程会这样做:
1 | var numbers = [1,2,3,4,5] |
而在声明式编程方式里,我们使用 reduce 函数:
1 | var numbers = [1,2,3,4,5] |
reduce 函数利用传入的函数把一个 list 运算成一个值。它以这个函数为参数,数组里的每个元素都要经过它的处理。每一次调用,第一个参数(这里是sum)都是这个函数处理前一个值时返回的结果,而第二个参数(n)就是当前元素。这样下来,每此处理的新元素都会合计到sum中,最终我们得到的是整个数组的和。
同样,reduce 函数归纳抽离了我们如何遍历数组和状态管理部分的实现,提供给我们一个通用的方式来把一个 list 合并成一个值。我们需要做的只是指明我们想要的是什么?
声明式编程语言:SQL
也许你还不能明白,但有一个地方,你也许已经用到了声明式编程,那就是SQL。
你可以把SQL当做一个处理数据的声明式查询语言。完全用SQL写一个应用程序?这不可能。但如果是处理相互关联的数据集,它就显的无比强大了。
像下面这样的查询语句:
1 | SELECT * from dogs |
如果我们用命令式编程方式实现这段逻辑:
1 | //dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ] |
我可没说SQL是一种很容易懂的语言,也没说一眼就能把它们看明白,但基本上还是很整洁的。
SQL代码不仅很短,不不仅容易读懂,它还有更大的优势。因为我们归纳抽离了how,我们就可以专注于what,让数据库来帮我们优化how.
我们的命令式编程代码会运行的很慢,因为需要遍历所有 list 里的每个狗的主人。
而SQL例子里我们可以让数据库来处理how,来替我们去找我们想要的数据。如果需要用到索引(假设我们建了索引),数据库知道如何使用索引,这样性能又有了大的提升。如果在此不久之前它执行过相同的查询,它也许会从缓存里立即找到。通过放手how,让机器来做这些有难度的事,我们不需要掌握数据库原理就能轻松的完成任务。