Python既可以看作一個實用(更好的庫)版本的Scheme,也可以看作一個凈化(沒有了“$@&%”符號)版本的Perl。然而Perl的哲學是TIMTIWTDI(there's more than one way to do it,即都種方法解決同意問題),Python試圖提供一個最小子集,使得人們以同樣的方式使用它(即TOOWTDI,there's only one way to do it,但是如果你努力嘗試的話,通常會有多種方式去解決同一問題)。
其中一個具有爭議的Python特征是,用縮進層次代替begin/end或者花括號,啟發(fā)它的哲學思想是:因為這里沒有括號,所以也就沒有了因為如何放置括號而引起的風格戰(zhàn)爭(style wars)。有趣的是,Lisp在這點上有同樣的哲學思想:每個人都用emacs去縮進代碼,所以他們沒有去爭論縮進方式。拿到一個Lisp代碼,對它進行合理的縮進,刪除行首的左括號(opening parens),以及與之匹配的閉括號(close parens),這樣我們就得到看起來類似Python的程序。
Python有做出明智妥協(xié)的哲學思想,使得容易的事情更容易,并且不排除太多困難的事情。在我看來Python做的不錯。簡單的事情簡單,困難的事情逐漸困難,并且你甚至注意不到這種不一致。Lisp有做出較少妥協(xié)的思想:提供一個強大并且完全一致的核心。這讓Lisp很難去學,因為你從一開始就操作在一個較高的抽象層次上,而且你需要理解你正在做什么,而不是僅僅依靠感覺活著看起來漂亮。但是它同樣意味著更容易為Lisp添加抽象層次和復雜性;Lisp優(yōu)化的目標是使得很困難的事情不太困難,而Python優(yōu)化的目標是使得中等難度的事情更簡單。
這里我從Python.org摘了一段對Python的簡介,并我創(chuàng)造了兩個版本:一個是用藍色斜體顯示的Python,另一個是用綠色粗體顯示的Lisp。其他大部分,兩個語言的共同部分,是黑色的。
Python/Lisp 是一個解釋型的和編譯型的, 面向?qū)ο蟮?,高級編程語言,并且擁有動態(tài)語義。它的高級的內(nèi)部數(shù)據(jù)結(jié)構(gòu),以及動態(tài)類型和動態(tài)綁定,使得它對于快速應用開發(fā)特別有吸引力,同樣地,Python作為腳本或者膠水語言去鏈接已存在的部分,也是非常有吸引力。Python/Lisp簡單、易學的語法強調(diào)的是可讀性,并因此降低程序維護的成本。Python/Lisp支持模塊(modules)和包(packages),這鼓勵了程序的模塊化和復用。Python/Lisp解釋器和廣泛的標準庫在大多數(shù)平臺上都是以源碼或者二進制形式發(fā)布的,并且可以自由的散發(fā)。通常,程序員愛上Python/Lisp是因為它提供地不斷增加的生產(chǎn)力。因為這里沒有分離的(separate)編譯步驟,“編輯-測試-調(diào)試”循環(huán)難以置信的快。調(diào)試Python/Lisp程序很簡單:一個bug或者壞輸入不會造成段錯誤。相反,當解釋器發(fā)現(xiàn)一個錯誤,它拋出一個異常。當程序沒有抓獲這個異常時,解釋器將打印棧軌跡。代碼級別的調(diào)試允許查看局部和全局變量,求值任意表達式,設置斷點,單步執(zhí)行,等等。調(diào)試器是由Python/Lisp自己寫的,表明了Python/Lisp的自省能力。另一方面,通常最快的調(diào)試一個程序的方法是向源碼中添加一些打印語句:快速的“編輯-測試-調(diào)試”循環(huán)使得這個簡單的方法特別有效。
我只添加下面這句:
盡管有些人對于縮進作為塊結(jié)構(gòu)/括號有原始的抵制,但是大多數(shù)人最后喜歡/深深的感激 他們。
想學習更多關(guān)于Python的知識,如果你是一個有經(jīng)驗的程序員,我推薦你去Python.org的下載頁面,下載文檔包,并且特別要注意Python參考手冊和Python庫參考。這里有各種各樣的輔導材料(tutorials)和出版的書籍,但是這些參考是你真正需要的。
下面的表作為一個Lisp/Python轉(zhuǎn)化的指南。紅色的表項標記了這個位置中的語言,明顯的比較糟糕(我自己的觀點)。粗體標記了這個位置上,兩個語言明顯不同,但是沒有一個方式明顯的好于另一個。用正常字體顯示的表項意味著兩個語言是相似的;語法可能稍有不同,但是概念是相同的,或者非常相近。表后面是一個要點列表和一些Python的示例程序.
關(guān)鍵特征Lisp 特征Python 特征
一切都是對象是是
對象有類型,變量沒有是是
支持異構(gòu)的(heterogenous)列表是 (linked list and array/vector)是 (array)
多范式語言是:函數(shù)式,命令式,OO,Generic是:函數(shù)式,命令式,OO
存儲管理自動垃圾收集自動垃圾收集
包/模塊使用困難使用簡單
對象,類的自省強大強大
元編程的宏強大的宏沒有宏
交互式的REPL(Read-eval-print loop)> (string-append 'hello' ' ' 'world')
'hello world'>>> ' '.join(['hello', 'world'])
'hello world'
簡介、富有表達力的語言(defun transpose (m)
(apply #'mapcar #'list m))
> (transpose '((1 2 3) (4 5 6)))
((1 4) (2 5) (3 6))def transpose (m):
return zip(*m)
>>> transpose([[1,2,3], [4,5,6]])
[(1, 4), (2, 5), (3, 6)]
跨平臺可移植性Windows, Mac, Unix, Gnu/LinuxWindows, Mac, Unix, Gnu/Linux
實現(xiàn)的數(shù)量很多一個主要的,附加一些分支(例如:Jython, Stackless)
開發(fā)模式專有和開源開源
效率大約比C++慢1到2倍大約比C++慢2到100倍
GUI, Web 等庫沒有標準GUI, Web 標準庫
方法
方法分派動態(tài), (meth obj arg) 語法
runtime-type, multi-methods動態(tài), obj.meth(arg) 語法
runtime-type, single class-based
數(shù)據(jù)類型Lisp 數(shù)據(jù)類型Python 數(shù)據(jù)類型
Integer
Bignum
Float
Complex
String
Symbol
Hashtable/Dictionary
Function
Class
Instance
Stream
Boolean
Empty Sequence
Missing Value
Lisp List (linked)
Python List (adjustable array)
Others42
100000000000000000
12.34
#C(1, 2)
'hello'
hello
(make-hash-table)
(lambda (x) (+ x x))
(defclass stack ...)
(make 'stack)
(open 'file')
t, nil
(), #() linked list, array
nil
(1 2.0 'three')
(make-arrary 3 :adjustable t
:initial-contents '(1 2 3))
很多 (in core language)42
100000000000000000
12.34
1 + 2J
'hello' or 'hello' ## 不可變的
'hello'
{}
lambda x: x + x
class Stack: ...
Stack()
open('file')
True, False
(), [] tuple, array
None
(1, (2.0, ('three', None)))
[1, 2.0, 'three']
很多 (in libraries)
控制結(jié)構(gòu)Lisp 控制結(jié)構(gòu)Python 控制結(jié)構(gòu)
語句和表達式一切都是表達式區(qū)分語句和表達式
假值(False)nil是唯一的假值False, None, 0, '', [ ], {}都是假值
函數(shù)調(diào)用(func x y z)func(x,y,z)
條件測試(if x y z)if x: y
else: z
條件表達式(if x y z)y if x else z
While循環(huán)(loop while (test) do (f))while test(): f()
其他循環(huán)(dotimes (i n) (f i))
(loop for x in s do (f x))
(loop for (name addr salary) in db do ...)for i in range(n): f(i)
for x in s: f(x) ## s為任意序列
for (name, addr, salary) in db: ...
賦值(setq x y)
(psetq x 1 y 2)
(rotatef x y)
(setf (slot x) y)
(values 1 2 3) 在棧上
(multiple-value-setq (x y) (values 1 2))x = y
x, y = 1, 2
x, y = y, x
x.slot = y
(1, 2, 3) 在堆中
x, y = 1, 2
異常(assert (/= denom 0))
(unwind-protect (attempt) (recovery))
(catch 'ball ... (throw 'ball))assert denom != 0, 'denom != 0'
try: attempt()
finally: recovery()
try: ...; raise 'ball'
except 'ball': ...
其他控制結(jié)構(gòu)case, etypecase, cond, with-open-file, etc.擴展的with語句
沒有其他控制結(jié)構(gòu)
詞法結(jié)構(gòu)Lisp 詞法結(jié)構(gòu)Python 詞法結(jié)構(gòu)
注釋;; 分號直到行尾## 井號直到行尾
界定符(Delimiters)用括號來界定表達式
(defun fact (n)
(if (<= n="" 1)="">
(* n (fact (- n 1)))))用縮進來界定語句
def fact (n):
if n <= 1:="" return="">
else: return n * fact(n — 1)
高階函數(shù)Lisp 高階函數(shù)Python 高階函數(shù)
應用函數(shù)
執(zhí)行一個表達式
執(zhí)行一個語句
加載文件(apply fn args)
(eval '(+ 2 2)) => 4
(eval '(dolist (x list) (f x)))
(load 'file.lisp') or (require 'file)apply(fn, args) or fn(*args)
eval('2+2') => 4
exec('for x in list: f(x)')
execfile('file.py') or import file
序列函數(shù)(mapcar length '('one' (2 3))) => (3 2)
(reduce #'+ numbers)
(every #'oddp '(1 3 5)) => T
(some #'oddp '(1 2 3)) => 1
(remove-if-not #'evenp numbers)
(reduce #'min numbers)map(len, ['one', [2, 3]]) => [3, 2]
or [len(x) for x in ['one', [2, 3]]]
reduce(operator.add, numbers)
all(x%2 for x in [1,3,5]) => True
any(x%2 for x in [1,2,3]) => True
filter(lambda x: x%2 == 0, numbers)
or [x for x in numbers if x%2 == 0]
min(numbers)
其他高階函數(shù)count-if 等
:test, :key 等關(guān)鍵字參數(shù)沒有其他內(nèi)置的高階函數(shù)
map/reduce/filter函數(shù)沒有關(guān)鍵字參數(shù)
Close over read-only var
Close over writable var(lambda (x) (f x y))
(lambda (x) (incf y x))lambda x: f(x, y)
Can't be done; use objects
參數(shù)列表Lisp 參數(shù)列表Python 參數(shù)列表
可選參數(shù)
變長參數(shù)
未確定的關(guān)鍵字參數(shù)
調(diào)用約定(defun f (&optional (arg val) ...)
(defun f (&rest arg) ...)
(defun f (&allow-other-keys &rest arg) ...)
只有明確聲明時,才可以用關(guān)鍵詞方式調(diào)用:
(defun f (&key x y) ...)
(f :y 1 :x 2)def f (arg=val): ...
def f (*arg): ...
def f (**arg): ...
用關(guān)鍵字方式調(diào)用任何函數(shù):
def f (x,y): ...
f(y=1, x=2)
效率Lisp 效率問題Python 效率問題
編譯
函數(shù)引用解析
聲明編譯到本機代碼
大多數(shù)“函數(shù)/方法”的查找很快
為了效率可以進行聲明編譯到字節(jié)碼
大多數(shù)“函數(shù)/方法”的查找比較慢
沒有聲明
特征Lisp 特征和函數(shù)Python 特征和函數(shù)
引用(Quotation)引用整個列表結(jié)構(gòu):
'hello
'(this is a test)
'(hello world (+ 2 2))引用單個的字符串或者.split():
'hello'
'this is a test'.split()
['hello', 'world', [2, '+', 2]]
自?。↖ntrospectible)文檔字符串(defun f (x)
'compute f value'
...)
> (documentation 'f 'function)
'compute f value'def f(x):
'compute f value'
...
>>> f.__doc__
'compute f value'
列表訪問通過函數(shù):
(first list)
(setf (elt list n) val)
(first (last list))
(subseq list start end)
(subseq list start)通過語法:
list[0]
list[n] = val
list[-1]
list[start:end]
list[start:]
哈希表訪問通過函數(shù):
(setq h (make-hash-table))
(setf (gethash 'one' h) 1.0)
(gethash 'one' h)
(let ((h (make-hash-table)))
(setf (gethash 'one' h) 1)
(setf (gethash 'two' h) 2)
h)通過語法:
h = {}
h['one'] = 1.0
h['one'] or h.get('one')
h = {'one': 1, 'two': 2}
列表上的操作(cons x y)
(car x)
(cdr x)
(equal x y)
(eq x y)
nil
(length seq)
(vector 1 2 3)[x] + y but O(n); also y.append(x)
x[0]
x[1:] but O(n)
x == y
x is y
() or [ ]
len(seq)
(1, 2, 3)
數(shù)組上的操作(make-array 10 :initial-element 42)
(aref x i)
(incf (aref x i))
(setf (aref x i) 0)
(length x)
#(10 20 30) 如果大小不變的話10 * [42]
x[i]
x[i] += 1
x[i] = 0
len(x)
[10, 20, 30]
對大多數(shù)人來說,一個重要的方面就是Python和Lisp相對于其他語言而言的執(zhí)行速度如何。我很難得到和你的應用相關(guān)的基準測試數(shù)據(jù),但是下面這個表可能對你有用:
5種語言在基準測試集The GreatComputer Language Shootout上的相對速度.測試LispJavaPythonPerlC++
哈希訪問1.063.234.011.851.00
異常處理0.010.901.541.731.00
圖例說明
對文件中的數(shù)字進行求和7.542.638.342.491.00
> 100 x C++
反轉(zhuǎn)行1.611.221.381.251.00
50-100 x C++
矩陣相乘3.308.90278.00226.001.00
10-50 x C++
堆排序1.677.0084.4275.671.00
5-10 xC++
數(shù)組訪問1.756.83141.08127.251.00
2-5 x C++
列表處理0.9320.4720.3311.271.00
1-2 x C++
對象實例化1.322.3949.1189.211.00
< 1="" x="">
單詞計數(shù)0.734.612.571.641.00
中位數(shù)1.674.6120.3311.271.00
25% to 75%0.93 to 1.672.63 to 7.002.57 to 84.421.73 to 89.211.00 to 1.00
范圍0.01 to 7.540.90 to 20.471.38 to 2781.25 to 2261.00 to 1.00
對速度進行了規(guī)格化處理,以使得利用g++編譯器編譯過的c++代碼的速度是1.00,所以2.00意味著比c++慢2倍;0.01意味著比c++快100倍。對于Lisp而言,使用的是CMUCL編譯器。背景顏色是按照右邊圖例說明進行繪制的。最后的3行給出了中位數(shù)值,25%到75%的quartile值(即去掉兩個最低值和去掉兩個最高值),以及整體范圍。在去掉兩個最低的和兩個最高的之后,比較Lisp和Python,我們發(fā)現(xiàn)Python比Lisp要慢3到85倍,Perl和Python差不多,但是比Java和Lisp都慢。Lisp大約比Java要快2倍。
給Lisp程序員的Python要點
在這里我列出了一些概念上的問題,是為像我一樣轉(zhuǎn)到Python的Lisp程序員準備的:
Lists are not Conses。 Python的列表其實比較像Lisp的可變數(shù)組,或者Java的向量(Vector)。這意味著列表的存取是O(1)的,但是與cons和cdr等價的操作卻產(chǎn)生了O(n)的新空間。你真的應該使用map或者for e in x:,而不是基于car/cdr的遞歸。注意Python里有多個空列表,而不是一個。這修正了Lisp一個常見的bug,即用戶調(diào)用(nconc old new),并期待old被修改了,但是當old是nil的時候,它并沒有被修改。在Python里,即使old是[ ],old.extend(new)也可以正常工作。但是這將意味著你必須用==測試列表是否為[];而不是用is,同時這也意味著,如果你把一個默認參數(shù)的值設置為[],你最好不要修改這個值。
Python is less functional。 部分原因是Python的列表不是conses,相比于Lisp,Python使用了更多方法去改變列表的結(jié)構(gòu),并且為了強調(diào)它們的改變,它們通常返回None值。例如像list.sort,list.reverse, 和list.remove這樣的方法都是,但是Python的新版本中引入了函數(shù)式的版本,即作為一個函數(shù)而不是方法,我們現(xiàn)在有了sortedandreversed(但是沒有removed)。
Python classes are more functional.在Lisp(CLOS)中,當你重定義一個類C時,表示類C的對象也相應地得到修改。已經(jīng)存在的C的實例和子類也因此重新指向新的類。這有時候會引起一些問題,但是在交互式的調(diào)試中,這卻是很有用的。在Python中,當你重定義一個類時,你會得到一個新的類對象,但是實例和子類還是指向舊類。這就意味著大多數(shù)時候你必須重新載入你的子類,并且重建你的數(shù)據(jù)結(jié)構(gòu)。如果你忘記了,將會引起混淆。
Python is more dynamic, does less error-checking.在Python中,對于未定義的函數(shù)或者域,或者傳給了函數(shù)錯誤的參數(shù)個數(shù),或者其他載入階段的其他大多數(shù)問題,你都不會得到任何警告信息;你必須等到運行時,才能得到錯誤信息。商業(yè)的Lisp實現(xiàn)將會把這些大多數(shù)問題標識為警告;簡單的Lisp實現(xiàn)(如clisp)不會。一個演示Python危險性的地方是,當你想寫self.field = 0時,卻敲入了self.feild = 0,后者將會動態(tài)的創(chuàng)建一個新的域。與之相對的Lisp等價物為,(setf (feild self) 0),它將給你一個錯誤。另一方面,訪問你一個未定義的域時,兩個語言都會報告一個錯誤。
Don't forget self.這點更應該引起Java程序員的注意:在一個方法中,確保你寫的是self.field,而不是field。這里沒有隱式的作用域(scope)。通常這會引起一個運行時錯誤。這很令人討厭,但是我認為過一段時間后,人們將學會不這么干。
不要忘記return.寫函數(shù)def twice(x): x+x是很誘人的,并且這不會發(fā)出任何警告或者異常信號,但是你可能真正想要的是def twice(x): return x+x。這點特別令人厭煩,因為在一個lambda表達式中,return語句是被禁止的,但是它的語義卻是執(zhí)行return。
注意單元素元組(tuple)。一個元組是一個不可變的列表,并且用圓括號圍起來,而不是用方括號。()是空元組,(1, 2)是一個含有兩個元素的元組,但是(1)表示的是1。而一個元素的元組卻要用(1,)表示,我擦!Damian Morton指出,如果你把元組看成是由逗號(,)產(chǎn)生的,打印時帶有圓括號的數(shù)據(jù),這樣就好理解了。圓括號只是起了消除歧義的作用,在這個解釋下,1, 2是含有兩個元素的元組,1,是含有一個元素的元組,圓括號有時是需要的,這主要取決于元組出現(xiàn)的位置。例如,雖然2, + 2,是一個合法的表達式,但是更加清晰的方式是(2,) + (2,)或者(2, 2)。
注意特定的異常 小心: 當key不存在時,dict[key]會拋出KeyError; lisp哈希表的用戶則期望得到nil。你應該捕獲這個異常或者用key in dict測試。
Python是Lisp-1的。 我的意思是Python只有一個命名空間,里面包含了變量和函數(shù),就像scheme一樣,而不是想Common Lisp那樣,有兩個命名空間。例如:
def f(list, len): return list((len, len(list))) ## bad Python(define (f list length) (list length (length list))) ;; bad Scheme(defun f (list length) (list length (length list))) ;; legal Common Lisp
對于域和方法也是一樣:你不能提供一個和域名相同的方法名:
class C: def f(self): return self.f ## bad Python ...
Python字符串不同于Lisp的符號(symbol)。Python通過把字符串內(nèi)化到模塊或類的哈希表中,然后進行符號查找。也就是說,當你寫obj.slot時,Python在運行階段會去obj類的哈希表中查找字符串'slot'。Python也會內(nèi)化一些用戶代碼中的字符串,例如,當你寫x = 'str'時。但是Python并不內(nèi)化那些看起來不像變量的字符串,例如x = 'a str'(感謝Brian Spilsbur指出這點)。
Python沒有宏(macro)。 Python可以訪問一個程序的抽象語法樹,但是這不是適合“心臟不好”的人。從積極的方面來看,該模塊容易理解,并且在5分鐘內(nèi),我用5行代碼就可以得到:
>>> parse('2 + 2')['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor', ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]
這令我相當失望,同樣的表達式在Lisp中的解析結(jié)果是(+ 2 2)??磥?,只有真正的專家才會想去操縱Python的解析樹,相反,Lisp的解析樹對任何人來說都是簡單可用。我們?nèi)稳豢梢栽赑ython中,通過連接字符串,來創(chuàng)建一些類似于宏的東西,但是它不能和其他語言集成,所以在實踐中不這樣做。在Lisp中,兩個使用宏的主要目的是:新的控制結(jié)構(gòu)和定制針對特定問題的語言。前者沒有在Python中實現(xiàn)。后者可以通過在Python中,用適合特定問題的數(shù)據(jù)格式來做到:下面我在Python中定義了一個上下文無關(guān)語法,分別通過1)組合字典的內(nèi)置語法,2)解析字符串的預處理過程完成的。第一種方式和Lisp的宏一樣優(yōu)雅。但是對于復雜的任務,例如為邏輯編程語言寫一個編譯器這樣的事,在Lisp中很容易,但是在Python將很困難。
比較Lisp和Python程序
我從《Paradigms of Artificial Intelligence Programming》一書中取了一個簡單的隨機句子生產(chǎn)器程序,并把它翻譯成Python。結(jié)論:簡介性相當;Python因為grammar[phrase]比(rule-rhs (assoc phrase *grammar*))簡單,而獲得一分,但是Lisp因為'(NP VP)比['NP', 'VP']更簡介而扳平比分。Python程序很可能比較低效,但是這不是我們關(guān)注的點。兩個語言看起來都很適合這樣的程序。調(diào)整瀏覽器窗口到合適的寬度以便閱讀代碼。
Lisp程序 simple.lispPython程序 simple.py
(defparameter *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.')(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))))(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))))(defun mappend (fn list) 'Append the results of calling fn on each element of list. Like mapcon, but uses append instead of nconc.' (apply #'append (mapcar fn list)))(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 this category.' (rule-rhs (assoc category *grammar*)))
from random import choicegrammar = dict( S = [['NP','VP']], NP = [['Art', 'N']], VP = [['V', 'NP']], Art = ['the', 'a'], N = ['man', 'ball', 'woman', 'table'], V = ['hit', 'took', 'saw', 'liked'] )def generate(phrase): 'Generate a random sentence or phrase' if isinstance(phrase, list): return mappend(generate, phrase) elif phrase in grammar: return generate(choice(grammar[phrase])) else: return [phrase] def generate_tree(phrase): '''Generate a random sentence or phrase, with a complete parse tree.''' if isinstance(phrase, list): return map(generate_tree, phrase) elif phrase in grammar: return [phrase] + generate_tree(choice(grammar[phrase])) else: return [phrase]def mappend(fn, list): 'Append the results of calling fn on each element of list.' return reduce(lambda x,y: x+y, map(fn, list))
Running the Lisp ProgramRunning the Python Program
> (generate 'S)(the man saw the table)
>>> generate('S')['the', 'man', 'saw', 'the', 'table']>>> ' '.join(generate('S'))'the man saw the table'
Python中的grammer比Lisp中的丑陋,這讓我很擔心,所以我考慮在Python中寫一個解析器(后來發(fā)現(xiàn)已經(jīng)有一些寫好的,并且可以免費獲得的),以及重載一些內(nèi)置的操作符。第二種方法在一些應用中是可行的,例如我寫Expr class,這是用來表現(xiàn)和操縱邏輯表達式的。但是對于這個應用而言,一個簡單、定制的語法規(guī)則解析器就夠了:一個語法規(guī)則是一個用“|”分開的,可選部分的列表,每個可選部分都是由空格(' ')分隔的單詞列表。把grammar程序重寫為一個更加符合Python慣用法的程序,而不是Lisp程序的翻譯,下面就是該程序:
Python Program simple.py (idiomatic version)
'''Generate random sentences from a grammar. The grammarconsists of entries that can be written as S = 'NP VP | S and S',which gets translated to {'S': [['NP', 'VP'], ['S', 'and', 'S']]}, andmeans that one of the top-level lists will be chosen at random, andthen each element of the second-level list will be rewritten; if it isnot in the grammar it rewrites as itself. The functions rewrite andrewrite_tree take as input a list of symbols. The functions generate andgenerate_tree are convenient interfaces to rewrite and rewrite_treethat accept a string (which defaults to 'S') as input.'''import randomdef make_grammar(**grammar): 'Create a dictionary mapping symbols to alternatives.' for (cat, rhs) in grammar.items(): grammar[cat] = [alt.split() for alt in rhs.split('|')] return grammar grammar = make_grammar( S = 'NP VP', NP = 'Art N', VP = 'V NP', Art = 'the | a', N = 'man | ball | woman | table', V = 'hit | took | saw | liked' )def rewrite(symbols): 'Replace each non-terminal symbol in the list with a random entry in grammar (recursively).' return [terminal for symbol in symbols for terminal in (rewrite(random.choice(grammar[symbol])) if symbol in grammar else [symbol])]def rewrite_tree(symbols): 'Replace the list of symbols into a random tree, chosen from grammar.' return [{symbol: rewrite_tree(random.choice(grammar[symbol]))} if symbol in grammar else symbol for symbol in symbols]def generate(symbols='S'): '''Replace symbol(s) in the space-delimited input string by a random entry in grammar (recursively until terminals); join back into a string.''' return ' '.join(rewrite(symbols.split()))def generate_tree(symbols='S'): 'Replace symbol(s) in the space-delimited input string by a random entry in grammar (recursively until terminals); return a tree.''' return rewrite_tree(symbols.split())