讓 if、else 帶有更明確的語意
November 4th, 2010Goto commentsLeave a comment
最近在維護程式時對於 if、else 有更深的體會,一但邏輯分支變多,很難釐清各種控制流程,一些簡單的習慣可以大幅簡化除錯和改程式的負擔。
變數的初始值
一個常見的情境是有個變數會依條件而有不同的值,典型的寫法如下:
1
2
3
4
5
# 假設後面有一長串算式會乘上 weight,這裡先決定它的值
if double:
weight = 2
else:
weight = 1
( 備註,在 Python 裡 if / else 裡設的變數和它的外層是同一個 scope。 )
或是善用程式語言提供的三元運算子設值 (即 ? : ),在 Python 裡則是這麼寫:
1
weight = 2 if double else 1
若有多種情況,在其它語言裡可能會用 switch,我個人不喜歡 switch,覺得用起來不直覺,Python 裡也沒有 switch,但可以用 dict 代替:
1
2
3
4
weight = {
'double': 2,
'triple': 3,
}.get(condition,1)
操作複雜時可在 dict 的 value 裡改用輔助的小函式,明確的用簡短的程式表明「這區塊在決定 weight 的值」。
別小看這一點小改變,當程式碼很多時,看到 “value = a if condition else b” 可以立即明白這裡的判斷式是用來設值,可以省下為 if、else 這區塊煩心的時間,也可以減少消耗精神和腦內暫存記憶。
提前處理簡單的分支
以用遞迴的方式實作費氏數列為例:
1
2
3
4
5
6
def fib(n):
if n < 0: # Error input.
raise ValueError('n must be positive.')
if n == 0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
上面的寫法先處理例外,接著就能放心處理正常的情況,再來處理特例 (初始值),最後就能專心和主邏輯奮戰,而覺得主邏輯變得單純許多,很好處理。
較大的程式,就是先寫幾個簡單輔助小函式 (例如 is_invalid()),先呼叫小函式避開特殊情況,一樣可以化繁為簡。
避免巢狀區塊和 continue、break
常見到在多層迴圈裡呼叫 if、else,並和 continue、break 混用,我個人覺得這種寫法很亂,而傾向用小函式 + return 避開使用 continue 或 break,比方像下面的程式要從一個兩層 list 裡找出每個 list 第一個負數,並算出負數的總和:
1
2
3
4
5
6
7
sum = 0
for numbers in a_list_of_numbers:
for n in numbers:
if n < 0:
break
if n < 0:
sum += n
可以改用小函式配合 return 避免使用 break 並「隱藏」分支:
1
2
3
4
5
6
7
8
9
10
def find_first_negative(numbers):
'''Return 0 if there is no negative number.'''
for n in numbers:
if n < 0:
return n
return 0
sum = 0
for numbers in a_list_of_numbers:
sum += find_first_negative(numbers)
改寫後,兩個 if 都不見了,主邏輯很清楚地表現出「找出各 list 第一個負數並加總」。
同樣的,程式愈複雜時,這些寫法省下的思考時間愈可觀。
明確的指明 else 的處理方式
這和前面提的東西有一點相衝突,視情況而定。在任何有 if、elif 的情況,即使 else 的情況不需做任何處理,仍要明確的寫出 else 並加上註解。如下所示:
1
2
3
4
5
6
if some_condition:
...
elif another_condition:
....
else: # Do nothing.
pass
這個作法的目的是,讓其他讀這份程式的人,明白原作者沒有漏考慮 else 的情況,不處理是符合預期的作法。函式愈長時,這樣寫的好處愈明顯。別小看這個小動作,程式碼一多,回頭讀程式碼時,這點小動作可以省下不少分心的機會。
結語
上面提的例子背後的目的都一樣,就是避免讀程式碼的人分心在分支裡,而能專注在主邏輯上。類似的例子還有「使用 iterator 少用 for + index」,平時留意一些小細節,不但能愈寫愈快 (省去煩心細節的時間),也能降低維護成本,讓其他人易於理解。舉手之勞做環保,大家一起來維護程式碼的品質吧!
歷史上的今天
2006-11-04 Information Flow - 2006
久違的1 on 1 - 2006
fcamelAll, Programming, Python, Software Engineering
Comments (3)Trackbacks (0)Leave a commentTrackback
clchiou
November 4th, 2010 at 23:47 | #1 Reply | Quote
那個語言 if/else 是不同 scope?
How about this
if cond1:
# some code
elif cond2:
# some code
# else: Do nothing
Hide else block in comments
fcamel
November 4th, 2010 at 23:58 | #2 Reply | Quote
我的意思是在 Python 裡, 下面的 code 是合法的:
1
2
3
if True:
x = 3
print x # value is 3
但在其它語言裡 if 和 for、while 一樣, 會成為一層新的 scope, 像在 C 裡這麼寫會 compile error:
1
2
3
4
if (1) {
int x = 3;
}
printf("%d\n", x);
直接寫註解表示 else 沒做事也不錯啊
clchiou
November 6th, 2010 at 02:11 | #3 Reply | Quote
You’re right. I forgot this.
沒有留言:
張貼留言