關於遞迴,有一句話是這麼說的:遞迴只應天上有,凡人應當用迴圈。從這句話就可以知道,遞迴這玩意兒,並不是那麼容易駕馭的。
先前寫遞迴函數,能夠寫出來就已經心滿意足了,根本不會去計較寫得好不好,有沒有什麼地方可以改善的。不過,最近倒是有機會開始思考,這遞迴函數到底要怎麼寫,才不會寫好之後總覺得虛虛的不踏實,沒辦法很清晰、很篤定地描繪出整個思考的過程。
事情是這樣的。在寫〈The Nature of Code閱讀心得與Python實作:8.2 Recursion〉時,書上有個範例是要畫這樣的一張圖:
把書上的遞迴函數改用Python來寫,長成這樣:
def draw_circles(surface, x, y, radius):
pygame.draw.circle(surface, (0, 0, 0), (x, y), radius, 1)
if radius > 4:
radius *= 0.75
draw_circles(surface, x, y, radius)
看到這段程式,心中不禁一陣狐疑,為什麼不像下面這樣寫呢?
def draw_circles(surface, x, y, radius):
if radius < 4:
return
else:
pygame.draw.circle(surface, (0, 0, 0), (x, y), radius, 1)
radius *= 0.75
draw_circles(surface, x, y, radius)
這樣子寫,遞迴函數最重要的出口,也就是base case,不是可以比較清楚地顯示出來嗎?這種風格的寫法,可讀性不是會比較高嗎?在想這問題的時候,突然想到〈PEP 8 – Style Guide for Python Code〉這份關於Python程式風格的文件。
在〈PEP 8 – Style Guide for Python Code〉這份文件中,列舉了不少增進程式可讀性的寫法。當然啦,既然主要的目的是增進程式的可讀性,所以不按照文件所建議的方式來寫,也不會怎樣;本來嘛!風格就純粹只是表現方式的不同,不涉及對錯的問題。不過,雖然程式的風格不影響對錯,但要能寫出可讀性高的程式,勢必得對要解決的問題及其解法有充分的了解,同時也要能靈活運用程式語言的語法。這也就是說,在思考怎樣的寫法可讀性較高的同時,其實也是在幫助自己釐清思路,避免腦袋瓜子打結。所以,在寫遞迴函數時,是不是也可以透過建立個人的風格,在提高程式可讀性的同時,也能夠讓思路清晰一點、腦袋瓜子少打一些結呢?
先前的那段程式看起來還不賴,不過後來決定要改成使用early return的寫法;像這樣:
def draw_circles(surface, x, y, radius):
if radius < 4:
return
pygame.draw.circle(surface, (0, 0, 0), (x, y), radius, 1)
radius *= 0.75
draw_circles(surface, x, y, radius)
之所以喜歡這樣的寫法,主要是因為可以拿掉else,減少縮排;每次看到一縮再縮的縮排都會眼花撩亂,說有多難過就有多難過。這樣的寫法另一個好處是,base case被獨立開來了,不管是在寫或在讀時,都能更專注在要處理的事情上。
所以,以後在寫遞迴函數時,就用early return的寫法,把base case寫在最前面。希望這樣子的風格,可以讓自己在寫遞迴函數時,能夠思路清晰腦袋不打結,寫完之後覺得踏實篤定一點。
關於遞迴,〈遞迴的美麗與哀愁〉這篇文章,頗值得一讀。