AI编程范式 第6章 构建软件工具(一)

第6章 构建软件工具

所谓人,就是能够使用工具的动物。没有工具就无从着力,有了工具则所向披靡。
——托马斯•卡莱尔(1795-1881)
在第4章和第5章,我们主要是构建了两个特定程序,GPS和ELIZA。在本章,我们来复习一下这两个程序,来找找看一些普遍的模式。这些从可重用软件工具中抽象出来模式将会在后续章节中起到一些帮助作用。

6.1 一个交互式的解释器工具

这个函数结构是eliza的一个通用版本,这里再提点一下:
(defun eliza ()
“Respond to user input using pattern matching rules.”
(loop
   (print ‘eliza>)
   (print (flatten (use-eliza-rules (read))))))

其他很多应用都是用了这种模式,包括Lisp本身。Lisp的顶层表现可以定义成这样子:
(defun lisp ()
(loop
   (print ‘>)
   (print (eval (read)))))

一个Lisp系统的顶层表现历史上一般就称作“读取-求值-打印 循环”这样的过程。大部分Lisp会在读取输入之前打印一个提示符,所以实际上应该是“提示符-读取-求值-打印 循环”才对,但是在一些早期的系统中,比如MacLisp就是没有提示符的。如果我们不考虑提示符的话,也可以仅用四个符号就写出一个完整的Lisp解释器:
(loop (print (eval (read))))
仅仅用这四个符号和八个括号就像构建一个Lisp解释器,听上去是像是在开玩笑。那这一行代码,到底是写出了什么意思呢?这个问题的一个答案就是去想象,如果用Pascal语言来写一个Lisp或者Pascal的解释器,我们究竟要做什么。需要一个词法分析器和一个符号表管理器。这些都是在工作的范围内,但是read可以处理这些东西。还需要语法分析器来聚合词法分隔符形成语句。Read也把这活儿干了,但只是因为Lisp的语句的语法是任意的,也就是列表和原子的语法。因此read在Lisp中扮演了一个很好的语法分析器的角色,但是在Pascal中是不行的。接下来,就是解释器的求值或者解释部分;eval完成了这项功能,并且也可以像处理Lisp表达式那样处理好Pascal的语句。Print所做的要比read和eval少得多,但是仍然是很有必要的。
重点并不是在于一行代码就可以被看做是一个Lisp的实现,而是要看做是计算过程的一般模式。ELIZA和Lisp都可以看做是交互式的解释器,读取一个输入,以某种方式转化或者求值输入,打印结果,之后返回等待更多的输入。我们从中可以提取出如下的一般模式:
(defun program ()
 (loop
    (print prompt)
    (print (transform (read)))))
有两种方式来利用这些递归模式:正规路子和野路子。先说野路子,将模式看做一个模板或是一种泛型,在程序设计过程中根据应用的不同屡次使用。当我们要写一个新程序,我们回想起写过的或者看到过的相似的程序,回头看看那个程序,吧相关的部分留下,之后修改为新程序做些修改就可以了。如果借用的程序部分比较多的话,在新程序中用注释标记一下原始程序的部分是一个比较好的做法,但是在原始程序和导出程序之间,是没有什么“官方”的连接的。
正规路子就是创建一种抽象,以函数的形式或者以数据结构的形式,显式地指向每一个新的应用——就是说,以一个可用软件工具的形式来适应抽象。解释器模式可以被抽象成一个如下的函数:
(defun interactive-interpreter (prompt transformer)
 “Read an expression, transform it, and print the result.”
  (loop
    (print pronmpt)
    (print (funcall transformer (read)))))
这个函数可以用来写每一个新的解释器:
(defun lisp ()
(interactive-interpreter ‘> #’eval))

(defun eliza ()
(interactive-interpreter ‘eliza>
#’(lambda (x) (flatten (use-eliza-rules x)))))
或者,可以借用高阶函数compose:
(defun compose (f g)
“Return the function that computes (f (g x)).”
#’(lambda (x) (funcall f (funcall g x))))

(defun eliza ()
(interactive-interpreter ‘eliza>
(compose #’flatten #’use-eliza-rules)))

在正规路子和野路子之间有两个主要的区别。首先,他们看上去不一样。如果是一个简单的抽象,就像上面那个,读取一个有显式循环输入发热表达式要比读取一个调用interactive-interpreter的表达式简单得多,因为后者需要找到interactive-interpreter的定义,还要理解定义才可以。
另一个区别在维护性上体现出来。假设我们在交互式解释器的定义中遗漏了一个特性。比如说疏忽了Loop的出口。我们就需要假定,用户可以用一些中断信息按键来结束循环。一个比较干净的实现是允许用户给解释器一个显式的结束命令。另一个有用的特性就是在解释器内除处理错误。如果我们使用野路子,给程序添加一个这样的特性就不会影响其他程序了。但是如果我们使用正规路子,之后对interactive-interpreter的所有改动将会自动给所有使用它的程序带来新的特性。
后面的interactive-interpreter版本增加了两个新的特性。首先,他使用宏handler-case来处理错误。这个宏会先求值第一个参数,然后返回第一个参数的值。但是如果有错误发生的话,后面的参数就会根据已发生的错误进行错误条件检查。这么用的话,error会匹配所有的错误,才去的行动就是打印错误条件之后继续。
这个版本也允许提示字符串或者一个没有参数的函数,函数会被调用打印提舒服。函数prompt-generator,会返回一个函数来打印形式1,2等等的提示符。
(defun interactive-interpreter (prompt transformer)
“Read an expression, transform it, and print the result.”
(loop
   (handler-case
     (progn
       (if (string prompt)
         (print prompt)
         (funcall prompt))
       (print (funcall transformer (read))))
     ;; In case of error, do this:
     (error (condition)
       (format t “~&;; Error ~a ignored, back to top level.”
         condition)))))

(defun prompt-generator (&optional (num 0) (ctl-string “[~d] ”))
“Return a function that prints prompts like [1], [2], etc.”
#’(lambda () (format t ctl-string (incf num))))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容