段譽比劍:《誰說不能從武俠學程式》選摘(1)

2022-09-08 05:10

? 人氣

誰說不能從武俠學程式?(資料照)

誰說不能從武俠學程式?(資料照)

《天龍八部》第四十二回,段譽和慕容復在少室山上比武。慕容復又是使劍,又是用刀,又是拿出判官筆,連續更換幾種兵器。段譽呢?始終以一雙空手發射劍氣,用無意中學會的段家絕學「六脈神劍」對抗。原文說:

[啟動LINE推播] 每日重大新聞通知

這商陽劍的劍勢不及少商劍宏大,輕靈迅速卻遠有過之, 他食指連動,一劍又一劍的刺出,快速無倫。使劍全仗手腕靈活,但出劍收劍,不論如何迅速,總是有數尺的距離,他以食指運那無形劍氣,卻不過是手指在數寸範圍內轉動,一點一戳,何等方便?何況慕容復被他逼在丈許之外,全無還手餘地。段譽如果和他一招一式的拆解,使不上第二招便給慕容復取了性命,現下只攻不守,任由他運使從天龍寺中學來的商陽劍法,自是占盡了便宜。

段譽沒有正經學過武功,刀劍和拳腳一竅不通,如果一招一式與慕容復對打,很快會死在慕容復手下;如果只攻不守, 自顧自地將六脈神劍練一遍,慕容復反倒會被他的無形劍氣逼得手忙腳亂。段譽明不明白其中道理?當然明白,當時他腦子裡必定形成這種邏輯:

如果見招拆招,那麼結局是輸。

如果不見招拆招,那麼結局是贏。

以上邏輯可用Python 語句表達:

if 見招拆招: 

結局 = 輸

else: 

結局 = 贏

if... else... 句式叫做「判斷語句」,又叫「選擇語句」, 是所有高階程式設計語言都有的句式,也是程式設計時經常用到的句式。

當然,由於使用中文變數「結局」和中文運算式「見招拆招」,上述代碼無法運行,只能用於描述程式設計思路或程式結構。這類描述性代碼在設計程式方案時常常用到,被稱為「虛擬碼」,又稱「偽代碼」。真正動手程式設計時,我們將虛擬碼轉化成符合Python 語法規範的代碼:

if defense == True: 

result = 0 

else: 

result = 1 

defense 是「防守」的英文形式,意思接近於「見招拆招」。result 是「結果」的英文形式,意思接近於「結局」。這四行代碼裡創建defense 和result 兩個變數,且規定defense 是布林型變數,有True 和False 兩個值;result 是整型變數, 有0 和1 兩個值。

defense 的值為True,意思是段譽選擇見招拆招;defense 的值為False,表明段譽回避見招拆招;result 的值為0,表明段譽敗給慕容復;result 的值為1,意思是段譽贏了慕容復。

代碼裡有等號=,還有雙等號==,這兩種等號擁有不一樣的功能。在Python 語言中,= 叫做「賦值符號」,用來給變數賦值;== 叫做「比較符號」,用來比較左右兩邊的變數是否相等。if defense == True,就是讓直譯器或編譯器做比較,比較布林變數defense 是不是等於True。如果等於True, 則result = 0,將整型變數result 賦值為0。否則執行else 下面的語句result = 1,將整型變數result 賦值為1。

這四行代碼還可以寫得更緊湊一些,去掉= 和== 兩邊的空格:

if defense==True: 

result=0 

else: 

result=1 

去掉空格後,程式照樣正常運行,但代碼的可讀性差了一點點─ 前面的變數和後面的數值擠到一起,不好看,也不容易識別。所以,在賦值符號= 和比較符號== 兩邊留出空格, 算是一個良好的程式設計習慣。

還記得另一個好習慣嗎?先在直譯器裡編寫代碼,經調試通過,再複製貼上到編輯器裡。

打開Python 直譯器,輸入代碼,注意留出空格,並讓兩個result 設定陳述式保持同樣的縮進狀態:

>>> if defense == True: 

result = 0 

else: 

result = 1 

按確認鍵,直譯器竟然以紅字報錯:

Traceback (most recent call last): 

File “<pyshell#4>”, line 1, in <module> 

if defense == True: 

NameError: name ‘defense’ is not defined 

為什麼報錯呢?注意看提示:「NameError: name ‘defense’ is not defined」,命名錯誤,名叫「defense」的變數沒有被定義。確實,我們一上來就讓直譯器判斷defense 是否等於True,然而事先沒有給defense 這個布林變數賦值。

怎麼調試呢?當然是先給defense 賦值,再輸入判斷語句:

>>> defense = False 

>>> if defense == True: 

result = 0 

else: 

result = 1 

這回直譯器裡有了兩個代碼塊,前一個代碼塊defense = False 是賦值語句,具體含義是做出預設,讓段譽放棄見招拆招,只管自己耍劍。

按輸入鍵,直譯器不再報錯,但也有給出任何結果。再檢查一遍代碼,原來判斷語句裡只給result 賦值,卻忘了把賦過值的變數輸出到螢幕上,所以還要補充一行print 代碼:

>>> print(result) 

print 本義是「列印」,但做為Python 的常用內置函式, print() 並非將小括號裡的變數傳送給印表機,而是將其輸出到螢幕上。包括在Swift、Perl、VB、R 語言、Groovy、Lua 等程式設計語言中,print 同樣是最常用的螢幕輸出內置函式。而C 語言的螢幕輸出函式是printf,C++ 的螢幕輸出函式是cout。在Linux、Windows 和MacOS 等作業系統的shell 裡面, 輸出函式則是echo。

再按一次輸入鍵,Python 直譯器執行print(result) 這個輸出語句,報出結果:1。result 為1,表示段譽與慕容復比劍的結局是贏。

直譯器裡調試通過,說明代碼不再有bug,打開編輯器, 將正確的代碼複製過去,注意調整縮進格式:

defense = False 

if defense == True: 

result = 0 

else: 

result = 1 

print(result) 

按快速鍵F5,使程式運行起來,編輯器會彈出一個小小的對話方塊:第三章 控制語句,三招兩式 118 119 

「Source Must Be Saved OK to Save?」(原始程式碼必須保存,要選擇保存嗎?)當然要保存。點確定,給程式取一個合適的名字,例如「段譽比劍」,保存到合適的目錄下。保存後,後臺編譯器立刻啟動,將代碼翻譯成機器語言,交給記憶體執行,執行結果會在另一個視窗當中顯示出來:

=============== RESTART: 段譽比劍.py =============== 

結果只出一個數字1,我們懂得這個1 代表的含義(段譽贏),但別人未必懂,為了讓程式更加人性化,還要完善代碼。不妨將print 代碼塊擴充為另一個判斷語句,使整個程式變成這樣:

defense = False 

if defense == True: 

result = 0 

else: 

result = 1 

if result == 0: 

print(‘ 段譽將在比劍中輸給慕容復’) 

else: 

print(‘ 段譽將在比劍中勝過慕容復’) 

後一個判斷語句用來判斷result 的值,如果值為0,輸出「段譽將在比劍中輸給慕容復」,否則輸出「段譽將在比劍中勝過慕容復」。

按F5 運行,程式輸出的結果好懂多了:

=============== RESTART: 段譽比劍.py =============== 

段譽將在比劍中勝過慕容復

細究起來,這個程式還缺乏互動環節,因此缺乏實用價值。對段譽來說,需要的是一個能幫他做決斷的程式:只要他輸入比劍策略,程式就能預測他的比劍結局。Python 恰好有一個能接受使用者輸入的內置函式input,該函式的語法規則是:

字串變數 = input(‘ 提示使用者輸入某些內容:’) 

還是在直譯器裡試用input 函式,先了解使用方法和實際功能,再回到編輯器完善代碼。試用過程從略,這裡直接給出完善後的代碼:

# 使用者輸入模組

choice = input(‘ 請段公子在此輸入比劍策略:’) 

# 程式處理模組

if choice == ‘ 見招拆招’: 

defense = True 

else: 

defense = False 

if defense == True: 

result = 0 

else: 

result = 1 

# 結果輸出模組

if result == 0: 

print(‘ 你將在比劍中輸給慕容復’) 

else: 

print(‘ 你將在比劍中勝過慕容復’) 

完善後的程式有了代碼注釋,還多出一行choice = input (‘ 請段公子在此輸入比劍策略:’)。這行代碼使用input 函式, 創建字串變數choice(選擇),提示段譽輸入比劍策略,輸入的內容將賦值給choice。

再次運行,跳出一行藍色的文字:

請段公子在此輸入比劍策略:

假設段譽在冒號後面輸入「見招拆招」,程式會告訴他:

你將在比劍中輸給慕容復

反之,如果段譽輸入「我自己耍劍」,程式回饋的結果必是「你將在比劍中勝過慕容復」。也就是說,我們編寫的程式終於有了實際功能─ 能讓段譽科學決策,避免被慕容復取走小命。

加上注釋,加上input 函式,再加上為了提高代碼可讀性而故意留出的空行,現在程式已經多達十九行。能否精簡一下呢?其實可以,應該將程式處理模組的兩個判斷語句合二為一,使代碼精簡到十一行:

# 使用者輸入模組

choice = input(‘ 請段公子在此輸入比劍策略:’) 

# 程式處理模組

if choice == ‘ 見招拆招’: 

defense = True 

result = 0 

else: 

defense = False 

result = 1 

# 結果輸出模組

if result == 0: 

print(‘ 你將在比劍中輸給慕容復’) 

else: 

print(‘ 你將在比劍中勝過慕容復’) 

代碼精簡後,程式功能沒有丟失或變弱,程式設計思路卻顯得更加清晰易讀。所以,在確保「程式功能不變」和「代碼清晰易讀」的前提下,能精簡一定要精簡,能把代碼寫短就盡量不要寫長,這是程式設計師應該遵守的另一個好習慣。

還能繼續精簡嗎?是的。

# 使用者輸入模組

choice = input(‘ 請段公子在此輸入比劍策略:’) 

# 程式處理模組

if choice == ‘ 見招拆招’: 

print(‘ 你將在比劍中輸給慕容復’) 

else: 

print(‘ 你將在比劍中勝過慕容復’) 

簡化到這個程度,程式處理模組和結果輸出模組合二為一,程式功能仍舊沒變,但卻削弱代碼的層次感。我們平常寫小程式不要緊,假如編寫幾百行、幾千行、幾萬行代碼,代碼必須層次分明、互不混淆。如果將處理模組和結果模組混到一起,將嚴重影響後期的調試和擴充。

用規範的方式寫程式,永遠讓代碼清晰易讀,是比「把代碼寫得更短」更重要的好習慣。例如,給變數a、b、c 賦值, 讓a 等於1,讓b 等於2,讓c 等於5,規範的寫法是:

a = 1 

b = 2 

c = 5 

養成壞習慣的程式設計師卻喜歡這樣寫:

a,b,c = 1,2,5 

對Python 編譯器來說,兩種寫法等價。但對程式設計師來說,前一種寫法顯然更加清晰,後一種寫法雖然省掉兩行代碼,卻增加其他程式設計師閱讀代碼的難度。

再例如,給變數a、b、c 重新賦值,讓a 的數值加1,讓b 的數值減2,讓c 的數值×7,規範的寫法是:

a = a+1 

b = b-2 

c = c*7 

而養成壞習慣的程式設計師往往會寫成這樣:

a += 1 

b -= 2 

c *= 7 

後面這種寫法也能被編譯器正常編譯,但形式上比較晦澀,走的是邪路,對初學程式設計的小朋友來說很不友善。要命的是,某些資深程式設計師偏偏拿邪路當標準,強迫團隊裡的新手去學習。就好比茴香豆的茴字有多種寫法,孔乙己偏偏使用最冷僻的寫法來記帳,並強迫酒店裡的小夥計也照著做。

江湖上常言:「與人方便,自己方便。」只有養成用規範方式寫代碼的習慣,才能讓別人看得懂代碼,才能讓團隊協作成為可能,才能為自己的工作帶來便利。

誰說不能從武俠學程式?-立體書封.jpg
誰說不能從武俠學程式?-立體書封.jpg

*作者李開周為青年學者本文選自作者著作《誰說不能從武俠學程式》(時報出版)

關鍵字:
風傳媒歡迎各界分享發聲,來稿請寄至 opinion@storm.mg

本週最多人贊助文章