#每週讀書挑戰#《黑客與畫家》

Cliffu發表於2020-03-12

提問:《黑客與畫家》中,作者曾在書中提到,“效率低下的軟體並不等於很爛的軟體" 這句話如何理解?作者的程式設計思維是?

答:理解這句話的關鍵是理解“效率低下”和“爛”,這是兩個有著相當廣闊含義外延的中文詞彙。如果脫離文字語境去闡述,就像單獨去解讀《論語》中的某個字某句話,可能和作者表達的本意有較大出入。

“效率低下”:這裡的效率低下不是貶義詞。作者說的效率低下不是指程式碼執行效率低下,比如一些沒有優化過的,時間複雜度和空間複雜度很高的演算法。效率低下是指為了架構的優美,可擴充套件而犧牲了一定程式碼效率。比如用C++寫一個計算圓形面積的程式,如果用用結構化程式設計的方法把圓形面積計算公式直接寫到程式裡肯定比用物件導向的方法創造一個Class AreaCalc並且寫一個AreaCircleCalc的function的程式碼效率要高,但是結構化的程式碼有侷限性,只能計算圓形的面積,後期程式碼維護起來很麻煩,所以作者認為有更好架構但是效率沒那麼高的程式碼更好。作者舉了Lisp語言裡string和list的例子,引入string型別是一種“效率很高”的方式,而用list生成字串是一種“效率很低”的方式。作者認為不引入string型別能保持基本運算子的簡潔性,他並不認可string這種“效率很高”的方式。更進一步說,所謂的“效率低下”只是體現在某一個小規模的具體問題計算上,如果考慮到程式的擴充套件及維護,那些針對具體問題優化到極致的程式反而會花掉程式設計師更多的時間(第一版開發+優化的時間以及後續痛苦的維護時間)。隨著計算機計算能力的大幅提升,犧牲一部分計算效率換取優良的程式碼架構是值得的。在4k記憶體的年代,可以把程式碼的優化精確到暫存器,但是後續任何改動都需要重寫程式碼,這樣的“效率高”反而是“效率低”。

“爛”“爛”是一個意思豐富的中文詞,翻譯的時候存在一定的資訊壓縮(並不是說阮一峰翻譯的不準確,因為語言表達習慣差異,這種資訊壓縮難免的)。《黑客與畫家》是作者博文的摘編,作者的部落格是免費的,可以很容易找到原文(原文連結http://www.paulgraham.com/hundred.html),附上這一整段:

Inefficient software isn't gross. What's gross is a language that makes programmers do needless work. Wasting programmer time is the true inefficiency, not wasting machine time. This will become ever more clear as computers get faster.

這裡“壞”用的是gross,同義詞是unpleasant,disgusting,讓人噁心,不舒服的意思。當你開啟一份沒有註釋,基本沒有命名習慣,類封裝隨意的程式碼時,一定可以體會到gross。可見這裡的“壞”並不是說軟體的執行效率很低或者bug很多,無法滿足目標需求,而是指程式碼的編寫習慣很差,讓人讀不下去。具體到這個語境下就是為了極限優化計算效率而犧牲可讀性,擴充套件性,可維護性的程式碼。goto語句是一個想當典型的例子,很多C/C++的科班學子可能在教材的第一頁上就看到了對goto的批判。程式設計師手動的使用goto可以一定程度上優化程式碼的效率(尤其是針對早期的編譯器以及早期捉襟見肘的硬體資源),但是大量的goto嚴重犧牲了程式的可讀性,大型程式幾乎無維護的可能,這樣的軟體看似efficient,但是gross。

總結一個程式的效率不光是指執行的機器時間,還要考慮到程式設計師編寫以及維護這個程式需要的時間。隨著計算機運算能力的大幅提高,程式設計師的時間會更加的寶貴。

作者的程式設計思維:好的程式語言以及好的程式碼應該是能夠幫助程式設計師節省時間的(包括開發和維護的時間),而不是為機器節省時間。程式設計的起點是一個高可靠性,可維護性,可複用的架構,具體填充程式碼實現反而是下一步的工作。


擴充練習試著畫出程式語言的進化脈絡

首先要討論一下“進化”這個詞。作者在The Hundred-year Language裡梳理了程式語言發展的歷史,談了自己的一些展望。中文裡“進化”是一個顯性的褒意詞,比以前變得更好了叫“進化”。作者用的詞是evolve,確切地說是“演化”,演化是一箇中性詞,單純表示發生了變化。馬哲裡講世界是運動的,但是隻有向上,向前的運動才是發展。evolve是運動,但不一定是發展。進化論是evolution,現在學界更傾向叫演化論。嚴復翻譯赫胥黎Evolution and Ethics時翻的特別好,叫《天演論》,他說演化不說進化。

程式語言的演化脈絡是降低編寫程式的難度,或者說降低程式設計師操作計算資源的難度,讓寫程式碼變得越來越輕鬆。這也符合人類社會的執行規律。80年代的汽車駕駛員需要具備很強的修車能力,考駕照還要考機械結構,但是這些科目都從現在的駕考中消失了。如果要求開汽車的人必須瞭解汽車的結構,這對於汽車普及是相當不利的。2012年360能夠完成珍珠港偷襲,主要得益於把軟體變得前臺妹子都能輕鬆使用(現在更進一步,連安裝都不需要你動手)。Ubuntu應當說為Linux推廣做了巨大貢獻,因為她讓Linux不再是專家級程式設計師的玩具。程式語言的發展也是類似,如果現在的程式開發還需要打紙帶,兒童程式設計基本沒有可能。

  • 第一代

第一代程式語言是機器語言,程式設計師直接和電子管,暫存器打交道。這樣的程式顯然書寫難度大,基本沒有維護的可能。

  • 第二代

第二代程式語言是組合語言,一些暫存器操作被封裝成MOV,ADD這種指令,用一個指令代替多條機器指令。

  • 第三代

第三代程式設計欲言是高階語言,進一步把MOV,ADD這種彙編指令封裝,程式設計師可以用接近自然語言的邏輯表示式書寫程式碼,由編譯器負責把高階語言的程式碼轉化成機器指令。高階語言也分很多流派,比如按照程式設計思想可以分為物件導向和麵向過程。由於物件導向的開發是目前軟體開發中的主流,所以大部分語言都有物件導向的特性。今年3月份TIOBE的top10裡只有C語言不是物件導向的,以往也基本如此。按照編譯方法分為解釋型和編譯型語言:解釋型的又叫指令碼語言,比如Python,Ruby,Perl;編譯型的比如C++,C#,Java等。一般地認為編譯型語言更底層,能操作更底層的硬體資源,執行效率高;而指令碼語言往往是為了解決一些具體的問題,所以有著豐富的特定場景解決方案,但是執行效率並不高。最典型的是python,由於有大量的科學計算,人工智慧,模式識別的庫,python幾乎是人工智慧的首選語言。當你使用一個更聰明的直譯器時,解釋型語言的效率劣勢能得到很大彌補,而且python相當多的底層函式庫也是用c/c++開發的,實現了使用效率和執行效率的平衡。可以預見在相當長的時間裡,程式語言的舞臺還是高階語言的天下,而且是百花齊放,各有所長。

  • 第四代 下一代程式語言是值得展望的事情。如果以100年為時間尺度,下一代程式語言很可能超出我們的想象,一如讓1920年代的人類想象智慧手機。下一代程式語言一定是打破馮諾伊曼計算架構的,它可能工作在量子計算機或者DNA 計算機平臺上。下一代便成語言的編寫門檻更低,我們可以用自然語言描述需求,由人工智慧負責程式碼的實現。可以肯定的是,下一代程式語言可以讓大部分人變得更懶,大部分普通程式設計師(或者人人皆可為程式設計師)用著極少部分精英程式設計師開發的工具(或者底層程式碼庫,由中級程式設計師編寫工具)書寫著大量程式碼。

相關文章