如果要给众多编程语言分个类,你可能会把它们分成低级语言和高级语言,或者分成面向对象语言和面向过程语言。然而,更多中国程序员所不太熟悉的另外一种划分方式将会把计算机语言分成命令式和声明式两大阵营。之所以说大家可能不太熟悉这种划分,那是因为我们平常所使用绝大部分语言都是命令式的。但事实上你确实也应该注意到另外一大阵营的存在。
命令式编程(ImperativeProgramming)是现今最为广泛使用的编程范型。读者所熟知的众多计算机语言,如C、C++、Java、Pascal、Basic、Python、Javascript等,都属于命令式编程语言的范畴。
与命令式编程相对立的是声明式编程(Declarative programming)。声明式编程与命令式编程有很大的不同。命令式编程是命令机器如何去做事情,这样不管你想要的是什么,它都会按照你的命令实现。声明式编程则是告诉机器你想要的是什么,让机器自己去想出如何做。(这对于用惯了命令式语言的程序员来说其实是一件非常难以想象的事情,不过后面我们会给出一个例子来演示这种“智能”)声明式编程描述目标性质,让计算机明白目标,而非流程。声明式编程不用告诉电脑问题领域,从而避免随之而来的副作用。而指令式编程则需要用算法来明确的指出每一步该怎么做。
声明式编程语言的代表有Prolog、Haskell、Erlang、Lisp等(其实还有一种大家可能更熟悉的,就是SQL语言)。我们还可以将这些语言分为两类,即函数式编程语言和逻辑式编程语言。熟悉计算机发展史的读者应该会知道,图灵采用图灵机来定义算法,而丘奇则采用Lambda演算来定义算法,而这两个定义是等价的(这也称为“丘奇-图灵论题”)。命令式编程是从图灵机的概念中延伸出来的;函数式编程则是以Lambda演算和函数的概念为基础发展而来的。当前比较流行的函数式编程语言代表非Haskell莫属。
图灵和丘奇
声明式编程语言中的另外一大阵营就是逻辑式编程语言,其中非常重要的一个代表就是Prolog语言。逻辑式编程是以德国数学家、逻辑学家戈特洛布·弗雷格(Gottlob Frege)提出的谓词演算和关系的概念为基础发展而来的。函数就是一种特殊的关系,它体现的是从输入到输出的一种单向关系,而当输入固定时,输出也唯一确定。关系并没有这些限制。从这个角度来说,逻辑式编程其实涵盖了函数式编程。
首届图灵奖的获得者,美国计算机科学家艾伦·佩利(Alan Perlis)曾言道:“如果一门计算机语言无法对你的编程思考方式产生影响,那么它就不值得你去学习”。Prolog或许就是一门非常值得你去研习的语言,因为它将完全颠覆你对于编程的既有认知。首先,Prolog并不是一个新兴的语言,相反,它其实是一种非常古老的语言。作为历史上出现的第一个逻辑式编程语言,Prolog最初是由艾克斯-马赛大学的阿兰·科尔默劳尔(Alain Colmerauer)等人在上世纪七十年代左右开发成功的。
其次,Prolog并不是什么很高深的语言,相反,与起其他一些程序语言(例如C++、Python等)相比, Prolog是更加容易理解的语言。如果你从来没有接触过计算机编程,那么恭喜你,你将很容易的进入Prolog的世界。如果你已经是其他命令式语言的高手,你就需要完全丢弃你原来的编程思路,否则是很难掌握Prolog的。
这不禁让我想起了金庸小说中老顽童周伯通自创的左右互搏术。据说周伯通曾被黄药师囚禁于桃花岛上,因为百无聊赖,遂别出心裁想到左手与右手打架,以自愉自乐。于是便自创了这门称为左右互搏术的奇学。后来,机缘巧合之下,负伤的周伯通与小龙女被金轮法王逼入绝境。小龙女与杨过曾经双剑合璧,大败金轮法王。而彼时杨过不在身边,小龙女孤掌难鸣,周伯通得知后便将左右互搏术传于小龙女。但周伯通也坦言道,黄蓉绝顶聪明,偏偏总不能得左右互搏术之要法,反倒是郭靖资质平庸,却能一学就会。而这另外一个学会了左右互搏术的便是心如止水的小龙女。最终小龙女一人分饰两角,使出玉女剑法,成功击溃了金轮法王。
说了这么多,最后我们以求解九宫格为例来演示Prolog如何能够智能地完成任务。在武侠小说《射雕英雄传》中有这样一个桥段。隐士瑛姑终日里苦修奇门异术,不想却被一道算题所困,虽百思而不得其解。问题最终被天资聪颖的黄蓉轻而易举的破解。九宫格是一种古老的数学游戏,它要求在三纵三横的一个矩阵中,填入从1到9这九个数字,并且纵向、横向、斜向上的三个数字之和皆相等。类似的问题在数学上被称为幻方。九宫格用现在的语言来描述,就是一个3×3的幻方问题。
如果你使用C++或者Python来编程,你会如何解决这个问题。如果我不告诉你所谓的“九宫格口诀”的话,你所想的事情当然是一步一步告诉计算机还如何做。事实上,即使我告诉你口诀 你也还是要让计算机根据口诀来一步一步的执行。当然,因为今天我们不是来讨论命令式语言的,所以我们就不具体演示C++或Python下的代码了。下面我们给出的Prolog的实现代码。当然考虑到读者可能从未接触过Prolog,我在其中加了注释。
:- ensure_loaded(library(clpfd)). puzzle_solution(Ls) :- %每行的和都相等 test_rows(Ls), %九宫格中的数字不会重复 check_difference(Ls), sum_diagonal(Ls, Sum1), transpose(Ls,Ts), %两条对角线上的和也相等 sum_diagonal(Ts, Sum1), %每列的和都相等 test_rows(Ts). sum_diagonal([[H|_]|[]], H). sum_diagonal([[H|_]|Ls], Sum) :- remove_heads(Ls, X), sum_diagonal(X, Sum0), Sum is H + Sum0. remove_heads([ ],[ ]). remove_heads([[_|T]|Rows],[T|X]) :- remove_heads(Rows,X). test_rows([]). test_rows([[]]). test_rows([[_|_]]). test_rows([X,Y|Ls]) :- initialize(X), initialize(Y), sum_list(X, S), sum_list(Y, S), test_rows([Y|Ls]). check_difference(Ls) :- append(Ls, L), is_set(L). %每个格都是从1到9中的一个整数 initialize([]). initialize([H|T]):- H in 1..9, label([H]), initialize(T).
是的,其实我们什么也没做,只是告诉计算机我们的要求,然后让计算机自己去想该怎么做。而且计算机确实做到了,如下图所示。注意那个false并不是错误的意思。由于九宫格的答案不是唯一的,尽管Prolog会把所有的答案都求出来,但是为了演示,我们还是希望它的解空间有一定限制。于是我们规定了其中两个值2和5。注意此处矩阵是用一个相当于数组的数组的形式来表示的。然后第一次执行,Prolog给出了一个答案,由于我们只想要一个答案就好,所以我们就输入了一个句号,表示OK可以,不用再显示其他答案了。第二次执行,在Prolog给出一个答案后,我们想问还有没有其他答案,所以我们用了一个分号,分号的意思就是问计算机还有没有其他的答案。Prolog回答我们说还有,所以它又给出了一个答案。(事实上这两个答案是互为转置的矩阵)当我们用分号再次询问还有没有更多答案时,Prolog回答我们说“已经没有更多答案了”,所以它返回一个false,所以false的意思就是没有更多答案了。
是不是顿时感觉Prolog很强大!事实上,Prolog还有一个更厉害的名字,叫人工智能语言,它在编写专家系统和人工智能应用方面非常非常常用,以后有机会,博主再奉上更多更炫的Prolog
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/baimafujinji/article/details/49745389