《AI编程范例:Common Lisp实例研究》前言和书评
本书的全称是:《Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp》,这是一本学习Lisp编程的优秀教科书,如果你已经学完了《Common.Lisp:A Gentle Introduction to Symbolic Computation》或者有同等的水平,这一本应当是最值得学习的。在这本书里,你所学习的不再是以语言为中心的语言教科书,而是面对真实问题,而且是编程中最有趣也最有挑战性的人工智能问题——古典AI。作者在书中介绍了上世纪第一个AI鼎盛期时所留下的遗产——基于规则的著名AI程序,全部是用Lisp写成,通过作者由简到繁由易到难的步步推进,不知不觉你的编程能力就会产生飞跃式提高。本书出版于1991年,那个时代的AI还是基于规则的理论体系,现在看现实意义似乎不大,但是背后的基本思想却永远不会过时。
好书应当分享,下面是我翻译的前言和我自己的感想。
本书有三个相关话题:AI、编程方法和Lisp。读者可以从本书学到AI的基本概念、理解AI的主要问题和技术,读懂重要的AI程序,掌握Common Lisp程序读、写、改的技能。
书中的例子的设计目标两个:良好编程风格范例——范例编程,同时也是AI研究的范例——所有程序都是历史上应用很广用以解决许多重要问题优秀范例。
在某个层次上,学校里的通识课程往往包含了关于某个文化的必读书籍,本书也一样,是关于AI文化的课程中的必读书籍。
在另一个层次上,本书又是高度技术性知识的汇集,使你从中级Lisp程序员成长为Lisp专家。第一部分旨在帮助新手起步,但是完全的门外汉会发现本书的内容并不适合自己。
计算机程序设计的教学在大多数情况下是由这样几个方面构成的:解释句法,向学生展示一个10行左右的程序,然后要求学生开始写程序。
而本书采用的理念是:学习写程序的最好方式阅读程序,反之,改善阅读程序的最好方式编写程序。因此,在用了最简短的方式介绍Lisp之后,我们将启程阅读复杂的程序,要求学生完全读懂,并可以做一些小的修改。
本书教学理念是:只有当你完全有能力鉴赏什么是优秀作品、并且自己心里有话题想要表达时才能写出有用的、令人感兴趣的东西。
这个理念适用于写诗,也适用于写程序。正像Kernighan和Plauger在《Software Tools in Pascal》的封面所提:
良好的编程不是学自泛泛而谈,而是利用我们的常识和良好的编程实践,通过观察优秀程序是如何读起来清晰、易懂,易维护、易修改、人性化、有效且可靠。彻底深入的研究和模仿优秀程序是改善编程技能的不二法门。
骄傲的工匠往往会展示自己最终的作品,而不会透露荒唐的起步和无数多的失误和挫折,而正是这些才是创造性活动不可避免的一部分。不幸的的是,羞于向别人展露这个过程是自己学习的真正障碍。一个学数学的学生在教科书上看到一个10行的漂亮证明,也许会赞叹其精妙,但很难学到如何自己建立这样的证明。
本书将试图展示编程活动的全过程,美丽作品后面的麻点和粉刺(warts and all)。
每章都从一个例示程序的最简版本开始,这个版本在有些情况下可以工作有些则失败。然后我们会分析失败的原因,然后逐渐建立更成熟的版本。这样,读者不但能理解程序最终版本的结果,而且学到如何从失败、改善的循环中从不成熟走向成熟。
如果你发现某一章比较难懂,可以先理解问题的难度,然后跳到下一章而不必被细节困扰。
本书所展示的内容大致属于“AI编程技术”,但这并不是绝对的,因为要成为优秀的AI程序员首先要使自己成为“好的程序员”,按照这个标准,本书的第三部分内容不属于AI,而是任何从事AI人员的基本背景知识。
为什么是Lisp,而且是Common Lisp?
Lisp是最古老但仍然活在当下的程序语言之一。Lisp有许多版本,这些版本有许多共同的特征,也有许多细节上的差异。本书采用的版本是Common Lisp,这个版本是最广为接受的版本。
我们采用Lisp作为教学语言有三点理由:
1 Lisp是AI最流行的语言,特别是在USA。如果你要学一门编程语言,当然要学习有生命力的,而不是学习死语。
2. Lisp特别适合定义新的对象时准确获得相关概念的一般性。Lisp还适合创建解决目标问题的新语言,这一点在AI应用中是非常有用的,特别是在对复杂的信息进行加工处理时可以用全新的形式表达。Lisp是少数可以最大限度地灵活定义程序和数据的语言。
3. 使用LIsp可以快速开发可应用的程序,因为Lisp程序简洁而不会因细节破坏整体的逻辑性。而Common Lisp提供了大量的、非比寻常的预定义对象包括700多个函数。Lisp具有良好的编程环境。
在欧洲和日本,Prolog在AI领域可能更流行,从灵活性和简洁性角度Prolog具有许多Lisp的优点。最近的趋势是,Prolog在美国开始流行,而Lisp开始在美国之外普及,从这个意义上,AI编程最好是双语。因此本书在11、12、20、21章专门讨论了Prolog的一些基本思想和应用。
和Common Lisp同时流行的Lisp方言还有Scheme,但只限于教学和实验。本书在22、23章介绍了Scheme。
有一种说法是,Lisp是专用语言而C、Pascal是通用语言,其实反过来才对。C和Pascal是对冯诺依曼体系内存、寄存器进行操作的“专用语言”,这些语言专注于算术和布尔表达式,尽管这些语言也提供一些手段建立数据结构,但是对过程性抽象或控制抽象却没有一个完整的机制。这些语言的设计是面向状态的编程风格,亦即、利用赋值语句,通过改变变量值计算结果。
而Lisp,没有特定的算术语句,加减乘除和其它所有表操作一样,例如字符串操作。但是,Lisp提供了编程所需的所有东西:定义数据结构、函数,以及将它们组合在一起的手段。
在Lisp中,那种以赋值语句为中心的、面向状态的编程风格仍然是可能的,不仅如此,Lisp也支持面向对象编程,基于规则编程、函数式编程等。这种灵活性来自于Lisp两个关键功能:强大的macro(宏),可用来扩展基本语言。当一种新的编程风格出现时,其它依附在这种风格之上的语言就会死亡,而Lisp通过定义新的macro,轻易接纳这种新风格。而macro的能力是建立在Lisp的简单的数据结构——list之上的。
Lisp的早期,是解释型语言,所有操作都建立在list之上。而现今的Lisp,更多的是编译型语言,Lisp程序员们更多的依赖于Lisp第二个优秀且灵活的特征:函数。当然函数并不是Lisp一家独有。而Lisp最特别的是,它允许程序在运行时创建新的函数。
Lisp的灵活性使它可以适应各种编程风格的变化,但更重要的是,它可以定制为解决特定问题的专用语言。其它语言使你的问题必须适合语言,而Lisp是使语言适合你的问题。
由于这些灵活性,Lisp一路走来,笑看了多少自己同辈、晚辈语言的产生、活跃、流行、衰退、死亡,真可以是“一壶浊酒”,古今多少事、都付笑谈中。现在,Lisp的最成功的方面是作为快速原型语言(rapid prototyping),研究探索型语言,适用于复杂、没有现成解决方案的问题表达,而AI正属于这类问题。
编程教学是本书的一大考量。Touretzky为初学编程着写的《Common Lisp: A Gentle Introduction to Symbolic Computation》强调的是简单性,写程序时稍微牺牲一点简洁性,而不是为了实现某个性能立刻引入比较难懂的概念。这种方法特别适合完全的初学者。
而本书的内容已经超越了完全初学者的水平,因此在新概念的引入上比前者更积极,只要是需要某种特性,就会引入相应的新概念、特性,而不会特意避开。不过有时专注于某一语言细节会使我们偏离话题,因此这个方面的知识需要读者参考其他语言手册自行补习。
本书概览
5大部分
第一部分:介绍Common Lisp语言,共有三章
第二部分:详解4个早期AI的著名程序,这些程序的特点就是基于规则模式匹配技术。解释方法是先给出一个简单的原型程序,然后逐渐增加复杂度,最终达到该程序当时达到的最高水平。通过这样的训练,读者可以由浅入深逐渐掌握高阶编程技巧。
这4个程序是:
GPS(General Problem Solver)通用问题解析器
ELIZA,模仿人类之间对话
STUDENT,可以解中学程度的代数应用题(word problem)
MACSYMA,解一般性的符号表示的代数问题,包括微积分。本书只给出该程序的一个子集。如果数学不好不是你的责任,你可以跳过这一章。
第三部分:关于编程的一般性话题
如果你完全理解并实践了本书的内容,可以被封为“高级Lisp程序员”
内容包括了程序性能提高的一些底层问题,例如缓存(caching)、索引化(indexing)、编译、和延迟计算等。
这部分还要介绍Prolog,以及Common Lisp中的对象系统:CLOS(Common Lisp Object System),最后对面向逻辑编程和面向对象编程的优缺点进行分析。并利用本部分所讨论的内容开发一个知识表达形式系统。
第四部分 高级AI程序
对MACSYMA的扩展
EMYCIN,一个专家系统的人机交互界面(shell)
自然语言处理,包括上下文无关语法,句法解析(parsing),使用Prolog的逻辑语法
第五部分 关于AI的一些周边问题
介绍Scheme,ANCI Common Lisp,以及Lisp程序的调试问题。
对这本书的看法
本书最好是看做Lisp的综合教科书,而不是AI(当代意义上的基于统计的AI),同时对于比较年轻的朋友,没有经历过早期基于规则的AI研究,现在通过Lisp的学习,体验一下当年前辈们是如何进行AI研究的。
本人推崇这本书是因为它的教学理念,这个理念就是:编程语言的教学应当与我们学习语文、学习外语的方法一致。要想写好文章,先要大量阅读,大量阅读是写作的基础。而目前流行的编程教育则把语法放到第一位,所有编程书籍都是以语法项目为核心展开的。而本书的方法则是,快速地给出最小核心语法,然后就是大量的阅读——阅读优秀作品,上世纪顶级人工智能专家开发的AI程序,从中学到最有用的编程技巧。更难能可贵的是,作者对这些程序的介绍不是一步到位,而是从最简化的核心部分讲起,然后慢慢增加复杂度,直到一个完整作品完成。这就像我们在学校时学语文,以范文为中心,分析文章的结构,中心思想,写作技巧,同时学习词汇、语法、修辞,最后是模仿范文自己写作文。从程序员角度,这样学习编程还有一个好处就是面向问题学习,而不是面向语言学习,就像我们学数学,除了会解公式题,还会解应用题,可以把对问题的自然语言描述翻译成数学语言。编程学习者可以把问题翻译成所学的语言,所学到的就是如何以编程语言解决问题。
本书另外一个最有价值处是对经典AI程序的介绍,这是其他任何编程书籍都找不到的宝贵资源。可以说,把书中所有程序读懂,实现一遍,等于基本上遍历了上世纪50-80年代AI的发展历程。书中对每个程序的介绍细致入微,可以说经过这样的历程,你的编程水平会戏剧性提高。
本书除了介绍Lisp语言,经典AI,还通过Lisp、Prolog和Lisp的CLOS,分别分析、比较了三种编程范式:函数式编程,面向对象编程和面向一阶逻辑的编程。这对任何对编程感兴趣的人无疑是很好的知识和见识的扩展。
最后说说书名:Paradigms of AI Programming。
作者在前言顶端,引用了朗曼词典对paradigm的释义:范例。本书所给出的程序无疑都是经典范例,本书就是围绕这些范例介绍Lisp语言,介绍经典AI基本思想的。我觉得这是展示编程语言知识的最好方法,从这个角度,这本书的境界高出了大部分同类编程书籍。如果你在学习Lisp,有了一些语言基础,这本书可以说是必看经典之一。
最最后说一下作者:Peter Norvig
是一位多产的计算机编程、AI作家,写过许多编程方面的专栏文章。对我印象最深的是那篇《Teaching yourself Programming in 10 years》,文章的标题明显是在讽刺那些书店中常见的“Teaching yourself XX Programming in 24 hours”系列,并提出了10000小时说。
这里是作者的博客http://norvig.com/
而关于AI话题,作者在进入21世纪后和Stuart Russell合作又出版了一部巨著(没错,超过1000页的巨著)介绍了关于AI的方方面面,是百科全书式的著作。
好书应当分享,下面是我翻译的前言和我自己的感想。
本书有三个相关话题:AI、编程方法和Lisp。读者可以从本书学到AI的基本概念、理解AI的主要问题和技术,读懂重要的AI程序,掌握Common Lisp程序读、写、改的技能。
书中的例子的设计目标两个:良好编程风格范例——范例编程,同时也是AI研究的范例——所有程序都是历史上应用很广用以解决许多重要问题优秀范例。
在某个层次上,学校里的通识课程往往包含了关于某个文化的必读书籍,本书也一样,是关于AI文化的课程中的必读书籍。
在另一个层次上,本书又是高度技术性知识的汇集,使你从中级Lisp程序员成长为Lisp专家。第一部分旨在帮助新手起步,但是完全的门外汉会发现本书的内容并不适合自己。
计算机程序设计的教学在大多数情况下是由这样几个方面构成的:解释句法,向学生展示一个10行左右的程序,然后要求学生开始写程序。
而本书采用的理念是:学习写程序的最好方式阅读程序,反之,改善阅读程序的最好方式编写程序。因此,在用了最简短的方式介绍Lisp之后,我们将启程阅读复杂的程序,要求学生完全读懂,并可以做一些小的修改。
本书教学理念是:只有当你完全有能力鉴赏什么是优秀作品、并且自己心里有话题想要表达时才能写出有用的、令人感兴趣的东西。
这个理念适用于写诗,也适用于写程序。正像Kernighan和Plauger在《Software Tools in Pascal》的封面所提:
良好的编程不是学自泛泛而谈,而是利用我们的常识和良好的编程实践,通过观察优秀程序是如何读起来清晰、易懂,易维护、易修改、人性化、有效且可靠。彻底深入的研究和模仿优秀程序是改善编程技能的不二法门。
骄傲的工匠往往会展示自己最终的作品,而不会透露荒唐的起步和无数多的失误和挫折,而正是这些才是创造性活动不可避免的一部分。不幸的的是,羞于向别人展露这个过程是自己学习的真正障碍。一个学数学的学生在教科书上看到一个10行的漂亮证明,也许会赞叹其精妙,但很难学到如何自己建立这样的证明。
本书将试图展示编程活动的全过程,美丽作品后面的麻点和粉刺(warts and all)。
每章都从一个例示程序的最简版本开始,这个版本在有些情况下可以工作有些则失败。然后我们会分析失败的原因,然后逐渐建立更成熟的版本。这样,读者不但能理解程序最终版本的结果,而且学到如何从失败、改善的循环中从不成熟走向成熟。
如果你发现某一章比较难懂,可以先理解问题的难度,然后跳到下一章而不必被细节困扰。
本书所展示的内容大致属于“AI编程技术”,但这并不是绝对的,因为要成为优秀的AI程序员首先要使自己成为“好的程序员”,按照这个标准,本书的第三部分内容不属于AI,而是任何从事AI人员的基本背景知识。
为什么是Lisp,而且是Common Lisp?
Lisp是最古老但仍然活在当下的程序语言之一。Lisp有许多版本,这些版本有许多共同的特征,也有许多细节上的差异。本书采用的版本是Common Lisp,这个版本是最广为接受的版本。
我们采用Lisp作为教学语言有三点理由:
1 Lisp是AI最流行的语言,特别是在USA。如果你要学一门编程语言,当然要学习有生命力的,而不是学习死语。
2. Lisp特别适合定义新的对象时准确获得相关概念的一般性。Lisp还适合创建解决目标问题的新语言,这一点在AI应用中是非常有用的,特别是在对复杂的信息进行加工处理时可以用全新的形式表达。Lisp是少数可以最大限度地灵活定义程序和数据的语言。
3. 使用LIsp可以快速开发可应用的程序,因为Lisp程序简洁而不会因细节破坏整体的逻辑性。而Common Lisp提供了大量的、非比寻常的预定义对象包括700多个函数。Lisp具有良好的编程环境。
在欧洲和日本,Prolog在AI领域可能更流行,从灵活性和简洁性角度Prolog具有许多Lisp的优点。最近的趋势是,Prolog在美国开始流行,而Lisp开始在美国之外普及,从这个意义上,AI编程最好是双语。因此本书在11、12、20、21章专门讨论了Prolog的一些基本思想和应用。
和Common Lisp同时流行的Lisp方言还有Scheme,但只限于教学和实验。本书在22、23章介绍了Scheme。
有一种说法是,Lisp是专用语言而C、Pascal是通用语言,其实反过来才对。C和Pascal是对冯诺依曼体系内存、寄存器进行操作的“专用语言”,这些语言专注于算术和布尔表达式,尽管这些语言也提供一些手段建立数据结构,但是对过程性抽象或控制抽象却没有一个完整的机制。这些语言的设计是面向状态的编程风格,亦即、利用赋值语句,通过改变变量值计算结果。
而Lisp,没有特定的算术语句,加减乘除和其它所有表操作一样,例如字符串操作。但是,Lisp提供了编程所需的所有东西:定义数据结构、函数,以及将它们组合在一起的手段。
在Lisp中,那种以赋值语句为中心的、面向状态的编程风格仍然是可能的,不仅如此,Lisp也支持面向对象编程,基于规则编程、函数式编程等。这种灵活性来自于Lisp两个关键功能:强大的macro(宏),可用来扩展基本语言。当一种新的编程风格出现时,其它依附在这种风格之上的语言就会死亡,而Lisp通过定义新的macro,轻易接纳这种新风格。而macro的能力是建立在Lisp的简单的数据结构——list之上的。
Lisp的早期,是解释型语言,所有操作都建立在list之上。而现今的Lisp,更多的是编译型语言,Lisp程序员们更多的依赖于Lisp第二个优秀且灵活的特征:函数。当然函数并不是Lisp一家独有。而Lisp最特别的是,它允许程序在运行时创建新的函数。
Lisp的灵活性使它可以适应各种编程风格的变化,但更重要的是,它可以定制为解决特定问题的专用语言。其它语言使你的问题必须适合语言,而Lisp是使语言适合你的问题。
由于这些灵活性,Lisp一路走来,笑看了多少自己同辈、晚辈语言的产生、活跃、流行、衰退、死亡,真可以是“一壶浊酒”,古今多少事、都付笑谈中。现在,Lisp的最成功的方面是作为快速原型语言(rapid prototyping),研究探索型语言,适用于复杂、没有现成解决方案的问题表达,而AI正属于这类问题。
编程教学是本书的一大考量。Touretzky为初学编程着写的《Common Lisp: A Gentle Introduction to Symbolic Computation》强调的是简单性,写程序时稍微牺牲一点简洁性,而不是为了实现某个性能立刻引入比较难懂的概念。这种方法特别适合完全的初学者。
而本书的内容已经超越了完全初学者的水平,因此在新概念的引入上比前者更积极,只要是需要某种特性,就会引入相应的新概念、特性,而不会特意避开。不过有时专注于某一语言细节会使我们偏离话题,因此这个方面的知识需要读者参考其他语言手册自行补习。
本书概览
5大部分
第一部分:介绍Common Lisp语言,共有三章
第二部分:详解4个早期AI的著名程序,这些程序的特点就是基于规则模式匹配技术。解释方法是先给出一个简单的原型程序,然后逐渐增加复杂度,最终达到该程序当时达到的最高水平。通过这样的训练,读者可以由浅入深逐渐掌握高阶编程技巧。
这4个程序是:
GPS(General Problem Solver)通用问题解析器
ELIZA,模仿人类之间对话
STUDENT,可以解中学程度的代数应用题(word problem)
MACSYMA,解一般性的符号表示的代数问题,包括微积分。本书只给出该程序的一个子集。如果数学不好不是你的责任,你可以跳过这一章。
第三部分:关于编程的一般性话题
如果你完全理解并实践了本书的内容,可以被封为“高级Lisp程序员”
内容包括了程序性能提高的一些底层问题,例如缓存(caching)、索引化(indexing)、编译、和延迟计算等。
这部分还要介绍Prolog,以及Common Lisp中的对象系统:CLOS(Common Lisp Object System),最后对面向逻辑编程和面向对象编程的优缺点进行分析。并利用本部分所讨论的内容开发一个知识表达形式系统。
第四部分 高级AI程序
对MACSYMA的扩展
EMYCIN,一个专家系统的人机交互界面(shell)
自然语言处理,包括上下文无关语法,句法解析(parsing),使用Prolog的逻辑语法
第五部分 关于AI的一些周边问题
介绍Scheme,ANCI Common Lisp,以及Lisp程序的调试问题。
对这本书的看法
本书最好是看做Lisp的综合教科书,而不是AI(当代意义上的基于统计的AI),同时对于比较年轻的朋友,没有经历过早期基于规则的AI研究,现在通过Lisp的学习,体验一下当年前辈们是如何进行AI研究的。
本人推崇这本书是因为它的教学理念,这个理念就是:编程语言的教学应当与我们学习语文、学习外语的方法一致。要想写好文章,先要大量阅读,大量阅读是写作的基础。而目前流行的编程教育则把语法放到第一位,所有编程书籍都是以语法项目为核心展开的。而本书的方法则是,快速地给出最小核心语法,然后就是大量的阅读——阅读优秀作品,上世纪顶级人工智能专家开发的AI程序,从中学到最有用的编程技巧。更难能可贵的是,作者对这些程序的介绍不是一步到位,而是从最简化的核心部分讲起,然后慢慢增加复杂度,直到一个完整作品完成。这就像我们在学校时学语文,以范文为中心,分析文章的结构,中心思想,写作技巧,同时学习词汇、语法、修辞,最后是模仿范文自己写作文。从程序员角度,这样学习编程还有一个好处就是面向问题学习,而不是面向语言学习,就像我们学数学,除了会解公式题,还会解应用题,可以把对问题的自然语言描述翻译成数学语言。编程学习者可以把问题翻译成所学的语言,所学到的就是如何以编程语言解决问题。
本书另外一个最有价值处是对经典AI程序的介绍,这是其他任何编程书籍都找不到的宝贵资源。可以说,把书中所有程序读懂,实现一遍,等于基本上遍历了上世纪50-80年代AI的发展历程。书中对每个程序的介绍细致入微,可以说经过这样的历程,你的编程水平会戏剧性提高。
本书除了介绍Lisp语言,经典AI,还通过Lisp、Prolog和Lisp的CLOS,分别分析、比较了三种编程范式:函数式编程,面向对象编程和面向一阶逻辑的编程。这对任何对编程感兴趣的人无疑是很好的知识和见识的扩展。
最后说说书名:Paradigms of AI Programming。
作者在前言顶端,引用了朗曼词典对paradigm的释义:范例。本书所给出的程序无疑都是经典范例,本书就是围绕这些范例介绍Lisp语言,介绍经典AI基本思想的。我觉得这是展示编程语言知识的最好方法,从这个角度,这本书的境界高出了大部分同类编程书籍。如果你在学习Lisp,有了一些语言基础,这本书可以说是必看经典之一。
最最后说一下作者:Peter Norvig
是一位多产的计算机编程、AI作家,写过许多编程方面的专栏文章。对我印象最深的是那篇《Teaching yourself Programming in 10 years》,文章的标题明显是在讽刺那些书店中常见的“Teaching yourself XX Programming in 24 hours”系列,并提出了10000小时说。
这里是作者的博客http://norvig.com/
而关于AI话题,作者在进入21世纪后和Stuart Russell合作又出版了一部巨著(没错,超过1000页的巨著)介绍了关于AI的方方面面,是百科全书式的著作。
实际上就古典人工智能而言, PAIP远不如Building Problem Solvers深刻, PAIP就像一本流行读物, 而BPS则是严肃的专著. (一个明显的地方就能看出这两者的差别, PAIP花了很多篇幅教你如何编写Lisp程序, BPS教都没教.) 就历史趣味而言, BPS也远胜PAIP. 我觉得为什么PAIP这么火不过是因为Peter Norvig本人在工业界比较有影响力罢了. (更让我恼火的是, PAIP的代码充斥着C++味, 我作为一个Schemer发现很难阅读它的代码, 我读BPS的时候就不存在这个问题, 虽然它们用的都是Common Lisp.)
> 我来回应