Wednesday, July 28, 2010

Elegant Code

每個Programmer,都應該是有能力分辨出一段code是否具有美感。只是有時候有人問我靠什麽去評判時,我一時無法詳細地去解釋,只能説是靠種感覺。現在仔細想來,優雅的Code應該是具有「簡潔明瞭精致有效」這八個字。

前四個字是做WhiteBox時的評判,只有Programmer在意,因爲他們看得到内部的code,他們需要clean code是因爲這樣他們可以更快速的去理解(無論是自己寫的還是別人寫的),從而能在理解基礎上快速修復或增加新的東西。

后四個字是BlockBox時的評判,User和Developer都在意,這關乎的是一個成品的表現。成品在意的是效率,就是速度與精確。而一般來説,聰明的算法與數據結構總是有更好的效率。換句話說,我們現代CPU架構的模式使得在相同標準下短小精悍的code有優勢。

這些東西說太多,也只是意會,不好理解,下面用四個例子來做更好的作説明。需要注意的是,這四段Code都是合理優秀的,裏面就算最普通的第一個也是有如教科書般經典。

 def f1(list):
string = ""
for item in list:
string = string + chr(item)
return string
 def f2(list):
string = ""
ichr = chr
for item in list:
string = string + ichr(item)
return string
 def f3(list):
return "".join([chr(item) for item in list])
 def f4(list):
return "".join(map(chr, list))

給一個相對大的list,f1-f4的運行時間分別是:
  • f1: 3.01409792928ms
  • f2: 2.99986600876ms
  • f3: 2.69300198555ms
  • f4: 1.84089493752ms
  1. f1是最正規的寫法,只要不是很performance intensive的程序,相信大多數Programmer都是這麽寫。

  2. f2只比f1相比只有一處變化,性能上也只是稍微快了一點點。因爲python的特性,當執行chr()這個function的時候,intepretor需要不斷向上回溯去找chr()被定義的地方。而f2則將local variable ichr指向了chr,避免了每個loop都要向上回溯的時間。當然,這個減少的時間微乎其微,因爲大多數compiler都會幫你優化。

  3. f1和f2還有一個地方是很浪費資源的,就是string concatenation。一般Programming初學者總是喜歡用這個方法:初始化一個empty的string,然後再把内容用+ operator粘貼在後面。這個方法在string相對小並且操作量少的情況下並無壞處。但是在上面的function裏,每個loop可能都以運行十萬計的情況下,就非常耗費資源。因爲其操作是每次重新初始化一個string在memory裏,然後把舊的string和新的string添加在一起放入那個memory位置。這樣的complexity是O(n**2)。而使用join()這個内建函數,其complexity則是O(n),因爲它一次性把所有要加的部份粘在一起。

  4. 這4個function裏面,以f4的字符數最少,但是速度提升最大的也是f4。f4比f3的改變僅僅是用map代替了for,如此小的改變獲得的效果比前兩個改變大得多。你可以把map當作是for被移到了C code裏了,唯一不好的地方是map的loop body必須得是一個function。結論就是一個program的效率最大影響的還是compiler,只要有内置相同作用的function,還是用内置的更快也更方便。

以上是我最近從http://wiki.python.org/moin/PythonSpeed/PerformanceTips所學到的小技巧,每种語言都有其類似的提升方法。

Good programming style的視覺美感不僅僅是如何format及indent,簡潔的code也總是能有更高的性能。如果要做一個更好的Programmer,光遵循書上或者老師教的好code是不夠的,優雅的code,那是必需的。

No comments:

Post a Comment