《天龍八部》第四十二回,段譽和慕容復在少室山上比武。慕容復又是使劍,又是用刀,又是拿出判官筆,連續更換幾種兵器。段譽呢?始終以一雙空手發射劍氣,用無意中學會的段家絕學「六脈神劍」對抗。原文說:
這商陽劍的劍勢不及少商劍宏大,輕靈迅速卻遠有過之, 他食指連動,一劍又一劍的刺出,快速無倫。使劍全仗手腕靈活,但出劍收劍,不論如何迅速,總是有數尺的距離,他以食指運那無形劍氣,卻不過是手指在數寸範圍內轉動,一點一戳,何等方便?何況慕容復被他逼在丈許之外,全無還手餘地。段譽如果和他一招一式的拆解,使不上第二招便給慕容復取了性命,現下只攻不守,任由他運使從天龍寺中學來的商陽劍法,自是占盡了便宜。
段譽沒有正經學過武功,刀劍和拳腳一竅不通,如果一招一式與慕容復對打,很快會死在慕容復手下;如果只攻不守, 自顧自地將六脈神劍練一遍,慕容復反倒會被他的無形劍氣逼得手忙腳亂。段譽明不明白其中道理?當然明白,當時他腦子裡必定形成這種邏輯:
如果見招拆招,那麼結局是輸。
如果不見招拆招,那麼結局是贏。
以上邏輯可用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,我們懂得這個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
後面這種寫法也能被編譯器正常編譯,但形式上比較晦澀,走的是邪路,對初學程式設計的小朋友來說很不友善。要命的是,某些資深程式設計師偏偏拿邪路當標準,強迫團隊裡的新手去學習。就好比茴香豆的茴字有多種寫法,孔乙己偏偏使用最冷僻的寫法來記帳,並強迫酒店裡的小夥計也照著做。
江湖上常言:「與人方便,自己方便。」只有養成用規範方式寫代碼的習慣,才能讓別人看得懂代碼,才能讓團隊協作成為可能,才能為自己的工作帶來便利。
*作者李開周為青年學者本文選自作者著作《誰說不能從武俠學程式》(時報出版)