2010年5月15日 星期六

用Python也能輕鬆玩自然語言處理(1.4)

1.4 回到Python:怎麼做選擇?怎麼控制流程

目前為止,我們已經學會去使用一些簡單的程式來處理語言,記住!我們正在學得是省去人力的自動方式唷!這種方式我們必須要告訴電腦更多的事情來讓他能夠根據我們給的指示去完成任務,如面臨不同狀況時能夠精準地進行決策、根據不同的條件反覆地執行迴圈內的工作等等,這些工作叫做「流程控制」,也是本節的重點!


條件式(Conditionals)


Python支援了許多的運算符來比較值與值之間的關係,常用的關係運算符如下表所示:

運算符號 代表關係
< 小於
<= 小於或等於
== 等於(是兩個等號喔!)
!= 不等於
> 大於
>= 大於等於

在之前幾節可以常常看見長這種樣子的例子:[w for w in text if condition ]這種條件式執行時會讓Python產生True或False的結果來進行判別。當然,除了用在數值比較上,還可以加入一些字串處理的函數來做這些判別的差事:

函數 意義
s.startswith(t) 在s中找出開頭為t的字串
s.endswith(t) 在s中找出結尾為t的字串
t in s t必須屬於s中
s.islower() 在s中找出所有字元都是小寫的字串
s.isupper() 在s中找出所有字元都是大寫的字串
s.isalpha() 在s中找出所有字元都是字母的字串
s.isalnum() 在s中找出所有字元同時有字母跟數字的字串
s.isdigit() 在s中找出所有字元都是數字的字串
s.istitle() 在s中找出所有為標題詞的字串(第一個字母為大寫)

這邊我們來舉出一些使用條件式在之前測試的文本中來選取所需的字吧!例如專門去找「ableness」結尾的字、包括「gnt」的字、標題字或是一些純數字的字串:

>>> sorted([w for w in set(text1) if w.endswith('ableness')]) 
['comfortableness', 'honourableness', 'immutableness', 'indispensableness', ...]
>>> sorted([term for term in set(text4) if 'gnt' in term]) 
['Sovereignty', 'sovereignties', 'sovereignty'] 
>>> sorted([item for item in set(text6) if item.istitle()])
['A', 'Aaaaaaaaah', 'Aaaaaaaah', 'Aaaaaah', 'Aaaah', 'Aaaaugh', 'Aaagh', ...] 
>>> sorted([item for item in set(sent7) if item.isdigit()]) 
['29', '61'] 
>>>


我們其實還可以產生更複雜的條件表達式,例如c代表一種條件,那「not c 」其實也是一種條件!如果希望兩個以上的條件時(如c1或c2),則可以利用「c1 and c2」或「c1 or c2」來合併!


處理更多的元素

在1.3的時候我們有看過一些除了文字以外的項目計算,現在我們學會了條件式,在更進一步來玩玩看吧!

>>> [len(w) for w in text1] 
[1, 4, 4, 2, 6, 8, 4, 1, 9, 1, 1, 8, 2, 1, 4, 11, 5, 2, 1, 7, 6, 1, 3, 4, 5, 2, ...] 
>>> [w.upper() for w in text1] 
['[', 'MOBY', 'DICK', 'BY', 'HERMAN', 'MELVILLE', '1851', ']', 'ETYMOLOGY', '.', ...]
>>>


現在我們來說明一下這些表達式的意含(如[f(w) for ...][w.f() for ...])。這裡f表示函數,可以用來做一些工作,像是計算長度的len()或轉換字串成為大寫的upper(),而w只是要表示一個條件中的變數宣告,告訴Python去選取text1中某些符合條件的字串並分派給w這個變數而已。不過就目前為止,你只需要會使用這些函數的功能即可,更多的語法細節(像是f(w)跟w.f()的差異等)你會從反覆的測試與學習中慢慢理解。

接著我們回到之前所探討過的詞彙長度以及一些搭配條件式後的應用:

>>> len(text1) 
260819 
>>> len(set(text1)) 
19317 
>>> len(set([word.lower() for word in text1])) 
17231 
>>>


這樣一來我們就省去了重複運算一些像是「This」與「this」這種只差一個大小寫的字彙長度,一比較之下就掃掉了約2000組字彙!我們甚至可以再進一步把數字或標點符號給過濾掉(只剩下真正由字母所組成的詞彙):

>>> len(set([word.lower() for word in text1 if word.isalpha()]))
16948 
>>>


或許你會開始覺得有些複雜,但其實這種「串列理解式(list comprehension)」的寫法會慢慢在之後的章節中陸續看到,久而久之你就會瞭解它的用法跟優點了!

巢狀區塊(Nested Code Blocks)

大部分的程式語言都會有所謂的「區塊(block of code)」,當一個條件表達式被滿足時就會執行。在前面我們已經看過許多條件式的範例(如「 [w forw in sent7 if len(w) < 4]」),而接下來的範例我們會創造一個叫做word的變數,內容為字串'cat',然後利用一個if條件式去檢查其長度是否小於5。如果符合(True)則會執行print那行的敘述列,並顯示出來一些訊息給使用者。記住!print那行的程式要縮排四格喔!!

>>> word = 'cat' 
>>> if len(word) < 5: 
...     print 'word length is less than 5' 
... 
word length is less than 5 
>>>

因為我們需要透過這些空白的縮排告訴Python這是一個巢狀的區塊!但如果我們把條件式稍微改成「len(word) >= 5」的話,這次word字串的長度必須要是大於5才會觸發print的執行,所以這次if敘述列的內容並沒有被執行,自然也沒有了訊息傳給使用者:

>>> if len(word) >= 5: 
...     print 'word length is greater than or equal to 5' 
... 
>>>


而這樣一個if的敘述列叫做控制結構,因為他決定了旗下那些縮排區域內的程式碼是否會被執行。接下來介紹另一個控制結構是for迴圈,試一試下面的例子,別忘了冒號跟縮排:

>>> for word in ['Call', 'me', 'Ishmael', '.']: 
...     print word 
... 
Call me Ishmael . 
>>>


這稱為迴圈的結構,因為Python會循環地去執行程式碼,首先會先把串列中的字串'Call'指派給變數word,並顯示出來。然後回到for那行敘述列,這次換成把字串'me'指派給word,一樣地再顯示出來給使用者,依此類推把串列中的所有項目處理完畢為止。

迴圈配條件

現在,我們剛剛學到的if for敘述結合起來,一樣地用迴圈把串列衝的每個項目都跑過,然後選擇印出只有為「l」結尾的字串!我們順便換一些變數名稱,以免Python被混淆:

>>> sent1 = ['Call', 'me', 'Ishmael', '.'
>>> for xyzzy in sent1: 
...     if xyzzy.endswith('l'): 
...         print xyzzy 
... 
Call Ishmael 
>>>


你應該已經發現在iffor的敘述列中都以冒號做結尾,開啟縮排程式碼的開始。事實上,所有的Python控制結構都以冒號做結的!用來提醒Python接下來的程式碼將會是縮排的區塊呈現。另外你應該也會很好奇當if描述式不被滿足時還可以做些什麼吧,其實會可以選擇性地使用「elif (else if)」或「else」敘述,注意他們也是需要冒號做結的喔!


>>> for token in sent1: 
...     if token.islower(): 
...         print token, 'is a lowercase word' 
...     elif token.istitle(): 
...         print token, 'is a titlecase word' 
...     else
...         print token, 'is punctuation' 
... 
Call is a titlecase word 
me is a lowercase word 
Ishmael is a titlecase word 
. is punctuation 
>>>


如你所見,利用一些小小的Python知識一樣可以寫出很多行的Python程式碼!這些細節是在將來你利用Python處理語言時十分重要的技能,你可以隨時在Python的互動式直譯器上先測試你想預測的結果(不用先寫入你的程式檔裡頭),這就是Python可愛又方便之處!

最後我們把這些學到東西都結合起來,首先利用串列理解式製作一個包括cie與cei的詞彙串列,接著利用迴圈逐一地把他們印出來。注意print那行結尾要加逗號,這是用來告訴Python,請把印出來的結果都放在同一行裡頭(不要印一個自動換一行):

>>> tricky = sorted([w for w in set(text2) if 'cie' in w or 'cei' in w]) 
>>> for word in tricky: 
...     print word, 
ancient ceiling conceit conceited conceive conscience conscientious conscientiously deceitful deceive ... 
>>>


沒有留言: