路遙知碼力,日久練成精- 精簡的程式碼不光是靠好用的內建函數拼湊出來,亦是對程式邏輯有足夠的理解的展現。
if- else 程式碼可說是工程師使用頻率很高,
卻也容易寫壞的一個例子,
過於多層的if-else嵌套,
容易使程式邏輯複輯,難以除錯
這一篇從語法及邏輯出發,教大家如何避免寫出冗長的if-else。
什麼?你說if-else有什麼難的,你第一個程式早就學過了?
首先來看昨天的課後習題的答案吧(昨日題目傳送門)。
參考解答:
def isLegal(s):
return s.replace('_','').isalnum() and not s[0].isdigit()
(注意:因為題目已經指明參數s是非空字串,可不必加上len(s)>0這個條件
看到這裡,有做昨天習題的朋友不禁要疑惑,
答案當真那麼簡單嗎?
你心中想的答案會不會是比較像底下這樣:
def isLegal(s):
if s.replace('_','').isalnum() and not s[0].isdigit():
return True
else:
return False
我們由淺入深來開始:
例如希望寫一個函數判斷一個整數是否是偶數,
常會看到經驗不足的人順手寫出以下程式:
def isEven(n):
if n%2 == 0:
return True
else:
return False
看起來是沒有什麼問題,
但其實n%2 == 0本身是一個條件判斷式,
n%2 == 0 的值已經是True或是False了,
因此這段程式可以簡單用一句話來寫:
def isEven(n):
return n%2 == 0
夠簡單吧?
以下舉幾個實例教大家化簡if-else,
希望能給大家一些啟發。
例如現在我們想要判斷一個變數型態是否為整數,
我們可以用type()函數檢查一個變數的型態。
化簡前:
n = 5
T=""
if type(n)==int:
T="n是整數"
else:
T="n不是整數"
化簡後:
n = 5
T= "n是整數" if type(n)==int else "n不是整數"
這個給個說明,
三元運算式的語法為:
變數 = 值1 if 條件式 else 值2
如果條件式的值為True,變數就會等於值1
,
否則變數等於值2
。
注意使用三元運算子語法時,
if-else一定要成對出現。
例如現在給你一個單字,
要你判斷這個單字的字首是不是母音('a','e','i','o','u')。
化簡前:
word = "apple"
if word[0] == 'a' or word[0] == 'e' or word[0] == 'i' or word[0] == 'o' or word[0] == 'u':
print("單字開頭為母音")
五個判斷式同樣以word[0]這個字來做等式判斷,
實在太冗長了。
可以用in
做改寫會簡單許多。
化簡後:
word = "apple"
if word[0] in 'aeiou':
print("單字開頭為母音")
in
這個關鍵字除了平時較常用的與for
搭配的語法外(例如for i in range(3):
),
亦可作為條件判斷,A in B
即是判斷物件A
是否在容器B
裡,
例如:
>>> 1 in [1,2,3]
True
>>> "a" in "apple"
True
民國1年是鼠年,
想要寫一個程式判斷今年是什麼年(鼠、牛、虎、兔、…)。
化簡前:
year = int(input())
if year%12 == 1:
print("今年是鼠年")
elif year%12 == 2:
print("今年是牛年")
elif year%12 == 3:
print("今年是虎年")
# …
#省略部分程式碼
# …
elif year%12 == 11:
print("今年是狗年")
elif year%12 == 0:
print("今年是豬年")
哇,光是判斷十二生肖就寫了12個if-elif了,
真是非常不簡潔呢,
我們看看如何簡化它。
化簡後:
year = int(input())
S = ["豬","鼠","牛","虎","兔","龍","蛇","馬","羊","猴","雞","狗"]
print("今年是"+S[year%12]+"年")
改成用列表取index的方式來做,
把if-elif全部省略了,
是不是非常簡潔易讀呢?
假設我們想把英文字母與撲克牌的花色做轉換,
且看以下程式:
化簡前:
letter = input()
suit = ""
if letter == 's':
suit = "黑桃"
elif letter == 'h':
suit = "紅心"
elif letter == 'd':
suit = "方塊"
elif letter == 'c':
suit = "梅花"
這時便很適合用python的字典化簡它。
化簡後:
letter = input()
D = {'s':"黑桃", 'h':"紅心", 'd':"方塊", 'c':"梅花"}
suit = D[letter]
不知道大家有看過遊戲王,
或是玩過卡片對戰類型的遊戲嗎?
這類遊戲因為卡片效果複雜的交互作用,
若是寫成if-else邏輯可能常常也是很複雜的。
舉例來說,假設我們想要做一款雙人對戰的卡片對戰遊戲,
創造一張卡片名稱叫作「俄羅斯輪盤」,
效果為「對任意角色造成傷害,此卡會重複施放十次。」
那麼每次判定的邏輯大概是長什麼樣子呢?
首先是卡片對戰的邏輯,
通常是打到其中一方人物生命值歸零,
遊戲就會結束,
這樣卡片效果可能就會在施放十次之前結束。
以下是我們設計的效果判定的邏輯:
先施放卡片效果。
若我方生命值小於0,回傳"輸掉遊戲",否則
若敵方生命值小於0,回傳"贏得遊戲",否則
若卡片效果已施放十次,回傳"結束效果",否則
回傳"繼續施放卡片效果"
這邊我們假設myHP
, enemyHP
, cnt
, 都是可取得的全域變數,
分別代表「我方生命值」、「敵方生命值」、「卡片施放次數」,
試著把它寫成程式邏輯:
化簡前:
def judge():
if myHP <= 0:
return "輸掉遊戲"
else:
if enemyHp <= 0 :
return "贏得遊戲"
else:
if cnt == 10:
return "結束效果"
else:
return "繼續施放卡片放果"
可以看到程式邏輯花了三層的if-else嵌套來做,
不是那麼好讀。
化簡後:
def judge():
if myHP <= 0:
return "輸掉遊戲"
if enemyHp <= 0 :
return "贏得遊戲"
if cnt == 10:
return "結束效果"
return "繼續施放卡片放果"
把多層if-else拆開後,整支程式就乾淨好讀多了。
這是python中還蠻特別的特性,
不一定每個程式語言都有。
在python中,and, or 進行的邏輯運算,
不一定總是返回True或是False這兩個值,
而是返回它們實際進行比較的值之一。
記住四個大原則,
>>> 1 and 2 and 3
返回3 (全為真值,返回最右邊的真值)
>>> False or []
返回 [] (全為假值,返回最右邊的假值)
>>> '0' or 0
返回 '0' ('0'不是空字串,是真值)
>>> 1 and {} and False and 3
返回 {} (返回第一個假值)
>>> 1 or 0 and 2
返回 1 (原式相當於 1 or (0 and 2) )
>>> 0 or 1 and 2
返回 2 (原式相當於 0 or (1 and 2) )
若你第一次看到這種機制,
可能會覺得奇怪,
為何規則2和規則3是這樣運作呢?
關鍵在and, or 的判斷中,
常常不需要檢查到最後一個條件即知道結果,
這邊and, or 各上一個生活化的例子來說明,
假設媽媽下廚,
想要煮牛排給家人吃,
但是食材並不會自己從天上掉下來,
所以媽媽需要去超商買牛排肉。
媽媽會出門買牛排肉需要同時滿足下面三個條件:
if 今天是晴天 and 超商有營業 and 牛排肉特價20%:
媽媽會出門買牛排肉
那麼如果第一個條件為假,
也就是今天下雨,
那麼已經可以確定媽媽是不會出門買肉的,
後面兩個條件根本不必檢查。
又或者今天雖然是晴天,
但是很不幸今天碰到國定假日,
超商沒有營業,
那麼牛排肉有沒有特價也就不必再檢查了,
這也就是為什麼and 總先返回第一個假值。
現今很多大學都設有英文畢業門檻,
意思是你必須任意選擇一種英文檢定考試通過,
才可以畢業。
假設有一間學校的規定需通過任一種測試方可通過英文畢業門檻:
if 全民英檢中級初試通過 or 多益700分以上 or 通過校內進修英文課程:
通過英文畢業門檻
那麼假設你已經通過全民英檢中級初試了,
學校便不必去檢查你有沒有考過多益英文考試,
或檢查你有沒有上過一門課叫做「進修英文」。
因為第一個條件滿足便確定通過門檻了,
這也就是為什麼or 總是返回第一個真值。
假設nums代表一個正整數的列表,
我們希望寫一個函數,
計算列表的數字總和。
若列表為空,則回傳-1。
直觀的寫法可能為
def sumOfList(nums):
return sum(nums) if len(nums)!=0 else -1
若是and, or高級語法則長這樣:
def sumOfList(nums):
return nums and sum(nums) or -1
今天就先學到這裡囉。
在曆法中,
若n是4的倍數但不是100的倍數,
或者n是400的倍數,
則n 為閏年。
我們希望實作一個函數輸入數字n,
回傳n是否為閏年,
實作如下:
def isLeapYear(n):
if n%4==0:
if n%100!=0:
return True
else:
if n%400 ==0:
return True
else:
return False
else:
return False
只是目前這個函數邏輯太複雜,
會讓讀者看的頭昏腦脹。
你能夠想到最精簡的寫法為何呢?
def isLeapYear(n):
year = int(n)
return (year % 400 == 0) or ((year % 100 != 0) and (year % 4 == 0))
ylist = [2000, 1999, 2001, 2004, 1900]
xlist = [(x, isLeapYear(x)) for x in ylist]
import calendar as cal
xlist2 = [(x, cal.isleap(x)) for x in ylist]
>>> print(xlist)
[(2000, True), (1999, False), (2001, False), (2004, True), (1900, False)]
>>> print(xlist2)
[(2000, True), (1999, False), (2001, False), (2004, True), (1900, False)]
我最近寫的主題也跟這個有關,寫程式到後面好像就越來越難駕馭邏輯了。
我目前的方向是朝「規則引擎」的方向走,希望大家以後寫程式不會再那麼痛苦了
def isLeapYear(n):
return True if not n%4 and n%100 or not n%400 else False