與其說語言影響思考,不如說不同語言中就該用不同的方法做事

前陣子被同事抓出來一段 code 有些問題。

我之前有一段 code,由於開始的時候對於整個程式的流程應該怎麼做不怎麼有把握,所以先用 Python 寫了 POC,覺得沒什麼問題之後,才用 Objective-C 重寫。裡頭有一段是這樣:我有兩個 array(當然,在 Python 裡頭用的是 list),叫做 a1 與 a2 好了,裡頭都是一堆字串,而我現在要把 a1 中拿掉所有出現在 a2 的內容;用 Python 想事情,自然就會想到使用 list comprehension 或是呼叫 filter()。

用 list comprehension 寫是這樣:

a3 = [x for x in a1 if x not in a2]

呼叫 filter() 則是這樣:

a3 = filter(lambda x: x not in a2, a1)

都是很簡單的程式,我後來選擇用 filter 萊寫。由於在寫 Python POC 的時候,腦袋裡頭想的就是如何對一個 list 下 filter,開始寫 Objective-C code 的時候,也就很自然想到,我應該呼叫 NSArray 的 filteredArrayUsingPredicate:。於是寫了一個用來過濾不在 a2 中物件的 NSPredicate,而且也進入了 production code。

NSPredicate *filter = [NSPredicate predicateWithFormat:@"NOT (SELF in %@)", a2];
NSArray *a3 = [a1 filteredArrayUsingPredicate:filter];

乍看之下沒什麼問題,實際跑起來也可以運作。但產品出貨一陣子之後,才意識到這邊有個很大的問題—如果資料量稍微大一點,不但執行速度慢,在一些 iOS 裝置上甚至可能造成 CPU 滿載,甚至造成應用程式 crash。我們換一種可以做到一模一樣效果的寫法:把 a1 先變成 mutable array,然後對這個 mutable array 呼叫 removeObjects。

NSMutableArray *a3 = [[a1 mutableCopy] autorelease];
[a3 removeObjectsInArray:a2];

兩者執行速度相差十幾倍!

於是我從中獲得了幾個教訓。在 Objective-C 中像是 filteredArrayUsingPredicate: 這類看起來很高階的寫法,或著可能其他 Key-Value Coding 的方式,想來在絕大多數狀況下還是少用為妙,即使似乎比較容易表達原本想要表達的意思,但看來有不小的效能陷阱。

至於多學幾門程式語言,的確可以在寫原本主要工作的語言時,受到來自其他語言的啟發,你也可以從中學到如何寫出精鍊的、富有表達能力、方便日後維護的程式碼,但是,程式碼最重要的意義,還是拿來執行。

Be Sociable, Share!

Leave a Reply