AI編程范式 第2章 一個(gè)簡(jiǎn)單的Lisp程序

第2章 這是一個(gè)簡(jiǎn)單的Lisp程序

Certum quod factum(吐槽:意大利語(yǔ)逼格是高呀~)
一切的本源都是自身的構(gòu)成(吐槽:格言什么的最難理解了)
-Giovanni Battista Vico(意大利皇家史官)
只學(xué)詞匯是永遠(yuǎn)不會(huì)精通一門外語(yǔ)的管削。必須要結(jié)合聽說讀寫奏瞬,才能精通一門語(yǔ)言竹揍。對(duì)于編程語(yǔ)言腕唧,道理也是一樣。
本章展示的是如何將普通函數(shù)與Lisp的特殊形式結(jié)合在一起器一,形成一個(gè)完整的程序霉晕。通過學(xué)習(xí)這個(gè)構(gòu)建的過程较店,Lisp剩下的詞匯你也會(huì)學(xué)好的笼痛。

2.1 英語(yǔ)的一個(gè)子集也要有語(yǔ)法

我們將要開發(fā)的程序會(huì)生成隨機(jī)的英語(yǔ)句子裙秋。下面是一個(gè)英語(yǔ)的子集的簡(jiǎn)單語(yǔ)法:
句子=>名詞短語(yǔ)+動(dòng)詞短語(yǔ)
名詞短語(yǔ)=>冠詞+名詞
動(dòng)詞短語(yǔ)=>動(dòng)詞+名詞短語(yǔ)
冠詞=>the,a…
名詞=>man,ball,woman,table…
動(dòng)詞=>hit,took,saw,liked…
技術(shù)上來講,上面的描述被稱作上下文無(wú)關(guān)的短語(yǔ)結(jié)構(gòu)語(yǔ)法缨伊,潛在的范式叫做生成型句法摘刑。這種概念是應(yīng)用在我們生成句子的時(shí)候,我們會(huì)生成一個(gè)動(dòng)詞短語(yǔ)刻坊,之后是一個(gè)名詞短語(yǔ)枷恕。名詞短語(yǔ)的定義就是冠詞后面加上一個(gè)名詞。冠詞的意思就是the谭胚,或者a或者其他冠詞徐块。這種形式主義是上下文無(wú)關(guān)的,因?yàn)閼?yīng)用的規(guī)則與所用的單詞無(wú)關(guān)漏益,這種生成的方法就是定義了一個(gè)語(yǔ)言的句法規(guī)則(相對(duì)的就是非句子的集合)蛹锰。接下來我們演示一下單個(gè)句子使用規(guī)則的應(yīng)用流程:
要獲得一個(gè)句子深胳,首先連接一個(gè)名詞短語(yǔ)和一個(gè)動(dòng)詞短語(yǔ)
要獲得一個(gè)名詞短語(yǔ)绰疤,連接一個(gè)冠詞和名詞
選擇冠詞the
選擇名詞man
名詞短語(yǔ)的結(jié)果是the man
為了獲得動(dòng)詞短語(yǔ),連接一個(gè)動(dòng)詞和一個(gè)名詞短語(yǔ)
選擇動(dòng)詞hit
獲取一個(gè)名詞短語(yǔ)舞终,連接一個(gè)冠詞和一個(gè)名詞
選擇冠詞the
選怎名詞ball
名詞短語(yǔ)結(jié)果是the ball
動(dòng)詞短語(yǔ)的結(jié)果是hit the ball
句子是the man hit the ball

2.2 一個(gè)直截了當(dāng)?shù)慕鉀Q方法

我們接下來開發(fā)的程序會(huì)用短語(yǔ)結(jié)構(gòu)語(yǔ)法生成隨機(jī)的句子轻庆。一個(gè)最直接的方法就是把每一個(gè)語(yǔ)法規(guī)則寫成一個(gè)獨(dú)立的Lisp函數(shù):
(defun sentence () (append (noun-phrase) (verb-phrase)))
(defun noun-phrase () (append (Article) (Noun)))
(defun verb-phrase () (append (Verb) (noun-phrase)))
(defun Article () (one-of ‘(the a)))
(defun Noun () (one-of ‘(man ball woman table)))
(defun Verb () (one-of ‘(hit took saw liked)))
每一個(gè)函數(shù)定義的參數(shù)列表都是空的,以為這他不接受任何參數(shù)敛劝。嚴(yán)格來說余爆,一個(gè)函數(shù)要是沒有參數(shù)輸入的話,它的返回值就是不變的值夸盟,所以我們會(huì)使用一個(gè)常量來代替蛾方。然而,這些函數(shù)會(huì)使用隨機(jī)函數(shù)上陕,因此桩砰,即使沒有參數(shù)輸入也會(huì)返回不同的值。這些函數(shù)在數(shù)學(xué)體系中是不存在的释簿,但是我們?nèi)匀环Q他們?yōu)楹瘮?shù)亚隅,因?yàn)樗麄兪菚?huì)返回一個(gè)值的。
現(xiàn)在剩下的就是去定義函數(shù)one-of庶溶,他接受的參數(shù)是一系列選擇煮纵,隨機(jī)返回其中的一個(gè)元素懂鸵。這個(gè)函數(shù)的隨后一部分是隨機(jī)選擇一個(gè)單詞返回一個(gè)列表,這樣就可以產(chǎn)生自由的互相組合了行疏。
(defun one-of (set)
?”Pick one element of set, and make a list of it.”
?(list (random-elt set)))
(defun random-elt (choices)
?”Choose an element from a list at random.”
? (elt choices (random (length choices))))
這里出現(xiàn)了兩個(gè)函數(shù)匆光,elt和random。Elt會(huì)從列表中選擇一個(gè)元素酿联。第一個(gè)參數(shù)是列表殴穴,第二個(gè)參數(shù)是元素的位置。容易有疏漏的地方是它的位置編號(hào)是從0開始货葬,所以表達(dá)式(elt choices 0)的結(jié)果是列表的第一個(gè)元素采幌,而表達(dá)式(elt choices 1)的結(jié)果是第二個(gè)元素。函數(shù)random的返回值是一個(gè)0到n-1之間的整型數(shù)震桶,所以表達(dá)式(random 4)返回的是0,1,2,3四個(gè)數(shù)當(dāng)中的一個(gè)休傍。
現(xiàn)在我們測(cè)試一下程序:
> (sentence) => (THE WOMAN HIT THE BALL)
> (sentence) => (THE WOMAN HIT THE MAN)
> (sentence) => (THE BALL SAW THE WOMAN)
> (sentence) => (THE BALL SAW THE TABLE)
> (noun-phrase) => <THE MAN)
> (verb-phrase) => (LIKED THE WOMAN)
> (trace sentence noun-phrase verb-phrase article noun verb) =>
(SENTENCE NOUN-PHRASE VERB-PHRASE ARTICLE NOUN VERB)
> (sentence) =>
(1 ENTER SENTENCE)
(1 ENTER NOUN-PHRASE)
(1 ENTER ARTICLE)
(1 EXIT ARTICLE: (THE))
(1 ENTER NOUN)
(1 EXIT NOUN: (MAN))
(1 EXIT NOUN-PHRASE: (THE MAN))
(1 ENTER VERB-PHRASE)
(1 ENTER VERB)
(1 EXIT VERB: (HIT))
(1 ENTER NOUN-PHRASE)
(1 ENTER ARTICLE)
(1 EXIT ARTICLE: (THE))
(1 ENTER NOUN)
(1 EXIT NOUN: (BALL))
(1 EXIT NOUN-PHRASE: (THE BALL))
(1 EXIT VERB-PHRASE: (HIT THE BALL))
(1 EXIT SENTENCE: (THE MAN HIT THE BALL))
(THE MAN HIT THE BALL)
程序運(yùn)行的還可以,追蹤信息的結(jié)果也和上面的理論推導(dǎo)和相似蹲姐,但是Lisp定義比語(yǔ)法規(guī)則來說會(huì)有一些難以閱讀磨取。問題隨著語(yǔ)法規(guī)則的復(fù)雜化慢慢變得復(fù)雜了。假設(shè)我們想要允許名詞短語(yǔ)之前可以加上不限數(shù)量的形容詞和介詞柴墩。用語(yǔ)法的表達(dá)形式忙厌,我們會(huì)得出下面的規(guī)則:
Noun-Phrase => Article + Adj* + Noun + PP*
Adj* => 0, Adj + Adj*
PP* => 0, PP + PP*
PP => Prep + Noun-Phrase
Adj => big, little, blue, green, . . .
Prep => to, in, by, with, . . .
在這個(gè)表達(dá)形式中,0的意思是不選擇江咳,逗號(hào)分隔的是多個(gè)選項(xiàng)中的一個(gè)選擇逢净,后面加上星號(hào)的意思是可選。星號(hào)的具體意思是可以不加歼指,也可以加多個(gè)爹土。這種標(biāo)記法叫做Kleene星號(hào)。之后我們會(huì)見到Kleene加號(hào)踩身,PP+的意思是一個(gè)或者多個(gè)的重復(fù)PP胀茵。
現(xiàn)在的問題是,對(duì)于形容詞和介詞的選擇挟阻,我們要使用Lisp的某種條件式來實(shí)現(xiàn):
(defun Adj* ()
?(if (= (random 2) 0)
??nil
??(append (Adj) (Adj*))))
(defun PP* ()
?(if (random-elt ‘(t nil))
??(append (PP) (PP*))
??nil))
(defun noun-phrase () (append (Article) (Adj*) (Noun) (PP*)))
(defun PP () (append (Prep) (noun-phrase)))
(defun Adj () (one-of ‘(big little blue green adiabatic)))
(defun Prep () (one-of ‘(to in by with on)))
之前選擇了兩個(gè)Adj和PP的實(shí)現(xiàn)琼娘,都是可以用的,下面兩個(gè)例子是錯(cuò)誤的附鸽,不可用的程序:
(defun Adj* ()
?“Warning – incorrect definition of Adjectives.”
?(one-of ‘(nil (append (Adj) (Adj*)))))
(defun Adj* ()
?“Warning – incorrect definition of Adjectives.”
?(one-of ‘( list nil (append (Adj) (Adj*)))))
第一個(gè)例子錯(cuò)在返回的是字面上的append表達(dá)式而不是一個(gè)計(jì)算后的值的列表脱拼。第二個(gè)定義會(huì)引起無(wú)限遞歸。關(guān)鍵點(diǎn)在于拒炎,隨著程序的漸漸開發(fā)挪拟,原來一些簡(jiǎn)單的函數(shù)將會(huì)變的復(fù)雜。為了理解他們我們需要理解很多Lisp的慣例-defun击你,()玉组,case谎柄,if,quote還有很多求值規(guī)則惯雳,并且必須要符合現(xiàn)實(shí)情況下的語(yǔ)法規(guī)則朝巫。程序越來越龐大,問題也會(huì)越來越難弄石景。

2.3 一個(gè)基于規(guī)則的解決方法

另一個(gè)實(shí)現(xiàn)的方法就是先關(guān)注語(yǔ)法的規(guī)則劈猿,之后再去考慮怎么個(gè)流程。我們來回顧一下原始的語(yǔ)法規(guī)則:
句子=>名詞短語(yǔ)+動(dòng)詞短語(yǔ)
名詞短語(yǔ)=>冠詞+名詞
動(dòng)詞短語(yǔ)=>動(dòng)詞+名詞短語(yǔ)
冠詞=>the,a…
名詞=>man,ball,woman,table…
動(dòng)詞=>hit,took,saw,liked…
這些規(guī)則的意思是左邊的是由右邊的組成的潮孽,有一點(diǎn)復(fù)雜的地方揪荣,右邊的構(gòu)成形式有兩種,一種是兩個(gè)對(duì)象的連接往史,比如名詞短語(yǔ)=>冠詞+名詞仗颈,另一種是列表名詞=>man,ball,。就實(shí)現(xiàn)來說椎例,每一種可能性都可以用列表中的元素表示挨决,至于兩個(gè)對(duì)象的連接,也可以展開订歪。下面是規(guī)則的代碼實(shí)現(xiàn):
(defparameter *simple-grammar*
?'(sentence -> (noun-phrase verb-phrase))
?(noun-phrase -> (Article Noun))
?(verb-phrase -> (Verb noun-phrase))
?(Article -> the a)
?(Noun -> man ball woman table)
?(Verb -> hit took saw liked))
"A grammar for a trivial subset of English.")
(defvar *grammar* *simple-grammar*
"The grammar used by generate. Initially, this is
這個(gè)規(guī)則的Lisp版本只是對(duì)原始規(guī)則的近似模仿脖祈。特別是符號(hào)->,沒有什么特別的意義,只是裝飾用的刷晋。
特殊形式操作符defvar和defparameter都會(huì)引入一個(gè)新的變量盖高,賦值給他;他們之間的區(qū)別是變量掏秩,grammer或舞,在程序運(yùn)行期間是可變的。而一個(gè)參數(shù)蒙幻,simple-grammar,一般來說是不可變的常量胆筒。更改一個(gè)參數(shù)被認(rèn)為是對(duì)程序本身的修改邮破,而不是程序修改值。
一旦規(guī)則的列表定義完成仆救,就可以用給定的目錄符號(hào)來修改了抒和。函數(shù)assoc就是干這個(gè)活兒的。Assoc接受兩個(gè)參數(shù)彤蔽,一個(gè)關(guān)鍵字和一個(gè)列表的列表摧莽,返回的是第一個(gè)關(guān)鍵字打頭的列表。沒有的話就返回nil顿痪。下面是例子:
> (assoc ‘noun *grammar*) => (NOUN -> MAN BALL WOMAN TABLE)
雖然用列表實(shí)現(xiàn)的規(guī)則很簡(jiǎn)單镊辕,但是定義函數(shù)油够,中間差一個(gè)操作語(yǔ)法規(guī)則抽象層還是很重要的。我們需要三個(gè)函數(shù):一個(gè)用來獲取規(guī)則右邊的對(duì)象征懈,一個(gè)用來獲取左邊石咬,還有一個(gè)根據(jù)目錄尋找可能的對(duì)象。
(defun rule-lhs (rule)
?”The lsft-hand side of a rule.”
?(first rule))
(defun rule-rhs (rule)
?”The right-hand side of a rule.”
?(rest (rest rule)))
(defun rewrites (category)
?“Return a list of the possible rewrites for the category.”
?(rule-rhs (assoc category *grammar*)))
定義的這些個(gè)函數(shù)會(huì)讓讀取規(guī)則的函數(shù)更加方便卖哎,更改規(guī)則也會(huì)更好用鬼悠。
現(xiàn)在,我們準(zhǔn)備好直接面對(duì)問題了:定義一個(gè)函數(shù)亏娜,生成句子(或者其他的短語(yǔ))焕窝。這個(gè)函數(shù)的名字叫做generate。他必須要應(yīng)付三種情況:1维贺,最簡(jiǎn)單的情況袜啃,一個(gè)相關(guān)規(guī)則的重寫集合的符號(hào)傳進(jìn)generate。根據(jù)這個(gè)參數(shù)隨機(jī)生成幸缕。2群发,如果符號(hào)不能重寫規(guī)則,就必須是一個(gè)終極符號(hào)-單詞或其他語(yǔ)法類別-就可以放一邊了发乔。事實(shí)上熟妓,我們返回的是輸入的單詞的列表。所有的結(jié)果都需要變成單詞列表的形式栏尚。3起愈,一些情況下,符號(hào)被修改的時(shí)候译仗,我們會(huì)選擇一個(gè)符號(hào)列表抬虽,并嘗試根據(jù)這個(gè)生成。因此纵菌,generate也必須接受一個(gè)列表作為輸入阐污,生成列表的每一個(gè)元素,聚合在一起咱圆。接下來對(duì)應(yīng)的部分是第一個(gè)對(duì)應(yīng)第三種情況笛辟,第二個(gè)對(duì)應(yīng)第一種,第三個(gè)對(duì)應(yīng)第二種序苏。在定義中可能會(huì)用到mappend函數(shù)手幢。
(defun generate (phrase)
?“Generate a random sentence or phrase”
?(cond ((listp phrase)
? ?(mappend #’generate phrase))
? ?((rewrites phrase)
? ?(generate (random-elt (rewrites phrase))))
? ?(t (list phrase))))
如數(shù)中許多程序一樣,函數(shù)篇幅很短忱详,信息量很大:編程的精髓在于知道什么該寫围来,什么不該寫。
這種編程風(fēng)格叫做數(shù)據(jù)驅(qū)動(dòng)編程,因?yàn)閿?shù)據(jù)(使用相關(guān)目錄重寫的列表)驅(qū)動(dòng)接下來的程序操作监透。在Lisp中這是一種自然易用的方式桶错,可以寫出精細(xì)可擴(kuò)展的程序,因?yàn)樵诓恍薷脑瓉沓绦虻幕A(chǔ)上加上一段新的數(shù)據(jù)也是可以的才漆。
下面是generate應(yīng)用的例子:
> (generate 'sentence)=>(THE TABLE SAW THE BALL)
> (generate 'sentence) ?=> (THE WOMAN HIT A TABLE)
> (generate 'noun-phrase) =>? (THE MAN)
> (generate 'verb-phrase) ?=> (TOOK A TABLE)
使用if來替代cond來寫generate也是可以的:
(defun generate (phrase)
?“Generate a random sentence or phrase”
?(if (listp phrase)
? ?(mappend #’generate phrase)
? ?(let ((choices (rewrites phrase)))
? ? ?(if (null choices)
? ? ? ?(list phrase)
? ? ? ?(generate (random-elt choices))))))
接下來是使用特殊形式let的版本牛曹,let引入一個(gè)新的變量(在這里是choices)并且給變量綁定一個(gè)值。在這種情況下醇滥,引入這個(gè)變量會(huì)減少兩次對(duì)rewrites的調(diào)用黎比,let形式的一般形式是這樣的:
(let ((變量 值)…))
?包含變量的函數(shù)體)
Let函數(shù)是對(duì)那些沒有參數(shù)的函數(shù)引入變量最常用的方式。一種竭力要避免的方式就是在引入變量之前嘗試去使用變量:
(defun generate (phrase)
?(setf choices …) ;;;wrong!
?… choices …)
因?yàn)檫@個(gè)符號(hào)choices現(xiàn)在指向一個(gè)特殊變量或者全局變量鸳玩,這個(gè)變量可能會(huì)被其它函數(shù)修改阅虫,所以這種方式要竭力避免。Generate函數(shù)現(xiàn)在還不可靠不跟,因?yàn)闆]有保證choices會(huì)在接下來的引用中一直保持同樣的值颓帝。使用let,我們會(huì)引入一個(gè)全新的變量窝革,沒有其他人可以訪問购城;因此就保證了他的值會(huì)保持下去。
【m】2.1 使用cond寫一個(gè)generate的版本虐译,但是要避免調(diào)用rewrites兩次瘪板。
【m】2.2 寫一個(gè)generate的版本,顯式區(qū)分終極符號(hào)(那些沒有重寫規(guī)則的符號(hào))和非終極符號(hào)漆诽。

2.4 之后的兩種思路

接下來程序的展現(xiàn)的兩種方式的兩個(gè)版本總是在程序開發(fā)的過程中一再出現(xiàn):(1)將問題直接描述成Lisp代碼侮攀。(2)使用最自然地方式描述問題,之后再來寫這種方式的解釋器厢拭。
第二種范式包括了一個(gè)額外的步驟兰英,因此更加是個(gè)規(guī)模較小的問題。然而供鸠,使用第二種思路的程序更加容易修改和擴(kuò)展畦贸。當(dāng)需要處理特別多的數(shù)據(jù)的時(shí)候,這種思路就比較管用回季。自然語(yǔ)言的語(yǔ)法實(shí)際上就是這種情況家制,大部分AI問題都適用這個(gè)描述。方法2背后的思想就是將問題盡可能限制在自己的術(shù)語(yǔ)環(huán)境內(nèi)泡一,并且將解決方法的核心最小化,用Lisp直接寫出來觅廓。
很幸運(yùn)的是鼻忠,Lisp設(shè)計(jì)一種新的表達(dá)法是很方便的,也就是設(shè)計(jì)一門新的編程語(yǔ)言。因此帖蔓,Lisp所鼓勵(lì)的是構(gòu)建更加穩(wěn)健的程序矮瘟。通篇都是這兩種方式。讀者會(huì)注意到塑娇,很多情況下澈侠,我們使用第二種。

2.5 不改變程序就能改變語(yǔ)法

哦我們來展示一下方法(2)的核心功能埋酬,定義一個(gè)新的語(yǔ)法哨啃,包含的有形容詞,介詞短語(yǔ)写妥,固有名稱和代詞拳球。之后可以將函數(shù)generate函數(shù)應(yīng)用,卻不用修改新的語(yǔ)法珍特。
(defparameter *bigger-grammar*
?‘((sentence -> (noun-phrase verb-phrase))
?(noun-phrase -> (Article Adj* Noun PP*) (Name) (Pronoun))
?(verb-phrase -> (Verb noun-phrase PP*))
?(PP* -> () (PP PP*))
?(Adj* -> () (Adj Adj*))
?(PP -> (Prep noun-phrase))
?(Prep -> to in by with on)
?(Adj -> big little blue green adiabatic)
?(Article -> the a)
?(Name -> Pat Kim Lee Terry Robin)
?Noun -> man ball woman table)
?(Verb -> hit took saw liked)
?(Pronoun -> he she it these those that)))
(setf *grammar* *bigger-grammar*)
> (generate ‘sentence)
(A TABLE ON A TABLE IN THE BLUE ADIABATIC MAN SAW ROBIN WITH A LITTLE WOMAN)
> (generate ‘sentence)
(TERRY SAW A ADIABATIC TABLE ON THE GREEN BALL BY THAT WITH KIM IN THESE BY A GREEN WOMAN BY A LITTLE ADIABATIC TABLE IN ROBIN ON LEE)
> (generate 'sentence)
(THE GREEN TABLE HIT IT WITH HE)
很明顯的是生成的句子有代詞問題祝峻,with he應(yīng)該是withhim才正確,才是正常的語(yǔ)法扎筒。隨機(jī)輸出的句子很顯然有時(shí)候是沒有什么意義的莱找。

2.6 對(duì)一些程序使用相同的數(shù)據(jù)

Another advantage of representing information in a declarative form-as rules or
facts rather than as Lisp functions-is that it can be easier to use the information for
multiple purposes. Suppose we wanted a function that would generate not just the
list of words in a sentence but a representation of the complete syntax of a sentence.
For example, instead ofthe list< a woman took a ba 11 ), we wantto getthe nested list:
聲明的形式(最為規(guī)則或者事實(shí)而不是函數(shù))來展現(xiàn)信息的另一個(gè)好處就是這些信息可以對(duì)對(duì)個(gè)目的使用。假設(shè)我們想要一個(gè)函數(shù)嗜桌,生成的不僅是單詞的列表還要有一個(gè)句子的完整語(yǔ)法奥溺。例如,列表(a woman took a ball)的嵌套形式就是:
(SENTENCE (NOUN-PHRASE (ARTICLE A) (NOUN WOMAN))
?(VERB-PHRASE (VERB TOOK)
??(NOUN-PHRASE (ARTICLE A) (NOUN BALL))))
對(duì)應(yīng)的語(yǔ)言意義樹可以這么描述:

2.1.2.JPG

使用上面的直接的方法的話症脂,我們會(huì)遇到很大的困難谚赎;不得不重寫每一個(gè)函數(shù)來生成另外的結(jié)構(gòu)。使用新的表示法诱篷,我們可以讓語(yǔ)法就保持原樣壶唤,只需要寫一個(gè)新的函數(shù):generate,可以生成嵌套列表的版本棕所。有兩個(gè)變化闸盔, 一個(gè)是使用cons將分門別類的重寫進(jìn)規(guī)則中,還有就是不用append聚合結(jié)果琳省,而是使用mapcar來列印他們迎吵。
(defun generate-tree (phrase)
?“Generate a random sentence or phrase,
?With a complete parse tree.”
?(cond ((listp phrase)
? ?(mapcar #’generate-tree phrase))
? ?((rewrites phrase)
? ?(cons phrase
? ? ?(generate-tree (random-elt (rewrites phrase)))))
? ?(t (list phrase))))
下面是一些運(yùn)行的例子:
> (generate-tree 'Sentence)
(SENTENCE (NOUN-PHRASE (ARTICLE A)
?(ADJ*)
?(NOUN WOMAN)
?(PP*))
(VERB-PHRASE (VERB HIT)
?(NOUN-PHRASE (PRONOUN HE))
?(PP*)))
? > (generate-tree 'Sentence)
(SENTENCE (NOUN-PHRASE (ARTICLE A)
?(NOUN WOMAN))
(VERB-PHRASE (VERB TOOK)
?(NOUN-PHRASE (ARTICLE A) (NOUN BALL))))
我們可以開發(fā)一個(gè)函數(shù)來生成一個(gè)短語(yǔ)的所有可能的改寫,作為單數(shù)據(jù)多程序方法的一個(gè)例子针贬。Generate-all函數(shù)返回一個(gè)短語(yǔ)的列表接著定義一個(gè)備用的函數(shù)combine-all击费,來管理結(jié)果的組合。實(shí)際上為了進(jìn)行空nil檢查桦他,是有四種而不是三種情況蔫巩。完整的程序仍然是很簡(jiǎn)短的。
(defun generate-all (phrase)
? “Generate a list of all possible expansions of this phrase.”
?(cond ((null phrase) (list nil))
? ?((listp phrase)
? ?(combine-all (generate-all (first phrase))
? ? ?(generate-all (rest phrase))))
? ?((rewrites phrase)
? ?(mappend #’generate-all (rewrites phrase)))
? ?(t (list (list phrase)))))
(defun combine-all (xlist ylist)
?“Return a list of lists forms by appending a y to an x.
?E.g., (combine-all ‘((a) (b)) ‘((1) (2)))
?->((A 1) (B 1) (A 2) (B 2)).”
? ?(mappend #’(lambda (y)
? ? ?(mapcar #’(lambda (x) (append x y)) xlist))
? ?ylist))
現(xiàn)在我們可以使用generate-all來測(cè)試原始的語(yǔ)法,知識(shí)有一個(gè)缺陷圆仔,generate-all是不能處理遞歸的規(guī)則的垃瞧,會(huì)導(dǎo)致無(wú)限的循環(huán)輸出。但是有限的語(yǔ)言是OK的坪郭。
> (generate-all 'Article)
( (TH E ) ( A) )
> (generate-all 'Noun)
((MAN) (BALL) (WOMAN) (TABLE))
> (generate-all 'noun-phrase)
((A MAN) (A BALL) (A WOMAN) (A TABLE)
(THE MAN) (THE BALL) (THE WOMAN) (THE TABLE))
> (length (generate-all 'sentence))
256
256個(gè)句子的意思是每一個(gè)句子都是這樣的結(jié)構(gòu)个从,冠詞-名詞-動(dòng)詞-冠詞-名詞,有兩個(gè)冠詞歪沃,四個(gè)名詞和四個(gè)動(dòng)詞嗦锐,所以結(jié)果就是2x4x4xx2x4=256。

2.7 練習(xí)題

【h】2.3 寫一個(gè)其他語(yǔ)言的語(yǔ)法绸罗,除了英語(yǔ)之外的意推,比如一種編程語(yǔ)言的子集。
【m】2.4 描述combine-all的一種方式就是兩個(gè)列表的向量叉乘之后在append珊蟀。寫一個(gè)高階函數(shù)croos-product菊值,用它來定義combine-all。
這個(gè)練習(xí)的目的就是讓你的代碼盡可能的通用育灸,因?yàn)槟阌肋h(yuǎn)不知道接下來會(huì)面對(duì)什么腻窒。

2.8 習(xí)題答案
2.1

(defun generate (phrase)
?“Generate a random sentence or phrase”
?(let ((choices nil))
? ?(cond ((listp phrase)
? ? ?(mappend #’generate phrase))
? ? ?((setf choices (rewrites phrase))
? ? ?(generate (random-elt choices)))
? ? ?(t (list phrase)))))

2.2

(defun generate (phrase)
?“Generate a random sentence or phrase”
?(cond ((listp phrase)
? ?(mappend #’generate phrase))
? ?((non-terminal-p phrase)
? ?(generate (random-elt (rewrites phrase))))
? ?(t (list phrase))))
(defun non-terminal-p (category)
?“True if this is a category in the grammar.”
?(not (null (rewrites category))))

2.4

(defun cross-product (fn xlist ylist)
?“Return a list of all (fn x y) values.”
?(mappend #’(lambda (y)
? ?(mapcar #’(lambda (x) (funcall fn x y))
? ? ?xlist))
? ?ylist))
(defun combine-all (xlist ylist)
?“Return a list of lists formed by appending a y to an x”
?(cross-product #’append xlist ylist))
現(xiàn)在我們可以使用另一種方式來使用cross-product:
> (cross-product #'+ '(1 2 3) '(10 20 30))
(11 12 13
21 22 23
31 32 33)
> (cross-product #'list '(a b c d e f g h)
'(1 2 3 4 5 6 7 8))
(A 1) (8 1) (C 1) (0 1) (E 1) (F 1) (G 1) (H 1)
(A 2) (8 2) (C 2) (0 2) (E 2) (F 2) (G 2) (H 2)
(A 3) (8 3) (C 3) (0 3) (E 3) (F 3) (G 3) (H 3)
(A 4) (8 4) (C 4) (0 4) (E 4) (F 4) (G 4) (H 4)
(A 5) (8 5) (C 5) (0 5) (E 5) (F 5) (G 5) (H 5)
(A 6) (8 6) (C 6) (0 6) (E 6) (F 6) (G 6) (H 6)
(A 7) (8 7) (C 7) (0 7) (E 7) (F 7) (G 7) (H 7)
(A 8) (8 8) (C 8) (0 8) (E 8) (F 8) (G 8) (H 8))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市磅崭,隨后出現(xiàn)的幾起案子儿子,更是在濱河造成了極大的恐慌,老刑警劉巖砸喻,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柔逼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡割岛,警方通過查閱死者的電腦和手機(jī)愉适,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來癣漆,“玉大人维咸,你說我怎么就攤上這事』菟” “怎么了癌蓖?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)婚肆。 經(jīng)常有香客問我租副,道長(zhǎng),這世上最難降的妖魔是什么较性? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任附井,我火速辦了婚禮讨越,結(jié)果婚禮上两残,老公的妹妹穿的比我還像新娘永毅。我一直安慰自己,他們只是感情好人弓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布沼死。 她就那樣靜靜地躺著,像睡著了一般崔赌。 火紅的嫁衣襯著肌膚如雪意蛀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天健芭,我揣著相機(jī)與錄音县钥,去河邊找鬼。 笑死慈迈,一個(gè)胖子當(dāng)著我的面吹牛若贮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痒留,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谴麦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了伸头?” 一聲冷哼從身側(cè)響起匾效,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恤磷,沒想到半個(gè)月后面哼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扫步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年魔策,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锌妻。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡代乃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仿粹,到底是詐尸還是另有隱情搁吓,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布吭历,位于F島的核電站堕仔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏晌区。R本人自食惡果不足惜摩骨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一通贞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恼五,春花似錦昌罩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至睬罗,卻和暖如春轨功,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背容达。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工古涧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人花盐。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓羡滑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親卒暂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啄栓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容