冒號和他的學生們(連載15)——數(shù)據(jù)類型

          冒號和他的學生們

          ——程序員提高班紀事


          1. 數(shù)據(jù)類型

          遵禮謂之劬,守法謂之固,此荒國之風也                        ——《荀悅·申鑒》


          待教室平靜下來,冒號再度開腔:“在談論動態(tài)語言之前,最好先澄清一下它與動態(tài)類型語言之間的區(qū)別。”

          嘆號訝然道:“它們不是一回事嗎?一直以為動態(tài)語言是動態(tài)類型語言的簡稱呢。”

          “有親戚之名,卻無血緣之親。名稱上相似,加之動態(tài)語言絕大多數(shù)確是動態(tài)類型語言,造成混淆實屬在所難免,但二者之間并無必然聯(lián)系——動態(tài)語言不一定是動態(tài)類型語言,動態(tài)類型語言也不一定是動態(tài)語言。”冒號飛跑的舌頭幾乎絆蒜,同時把眾人的腦子攪成了一鍋粥。

          見勢不妙,冒號改用迂回戰(zhàn)術(shù):“我們不妨再談開些,大家對數(shù)據(jù)類型是如何理解的?”

          逗號隨口道:“數(shù)據(jù)類型不就是數(shù)據(jù)的種類嗎?”

          眾人暗笑:說了跟沒說差不多。

          冒號說道:“數(shù)據(jù)類型包含兩個要素:一個是允許取值的集合,一個是允許參與的運算。例如int類型在Java中既定義了介于− 231 231 − 1之間的整數(shù)集合,也定義了該集合上的整數(shù)所能進行的運算?,F(xiàn)在的問題是:數(shù)據(jù)類型的意義何在?”

          句號回答:“限定一個變量的數(shù)據(jù)類型,就意味著限制了該變量的取值范圍和所參與的運算,這從一定程度上保證了代碼的安全性。”

          冒號追問:“還有嗎?”

          句號略作思考后說:“用戶自定義的數(shù)據(jù)類型,如C中的結(jié)構(gòu)和Java中的類或接口,賦予數(shù)據(jù)以邏輯內(nèi)涵,提高了代碼的抽象性。”

          “精辟!”冒號贊道,“數(shù)據(jù)類型既有針對機器的物理意義,又有針對人的邏輯意義。前者用于進行底層的內(nèi)存分配和數(shù)值運算等,后者用于表達高層的邏輯概念。既然類型如此重要,類型檢查就必不可少了。所謂動態(tài)類型語言Dynamic Typing Language),正是指類型檢查發(fā)生在運行期間run-time)的語言。”

          “那靜態(tài)類型語言Static Typing Language)自然是類型檢查發(fā)生在編譯期間compile-time)的語言咯。”引號接話道。

          冒號回應:“一般的說法是這樣,不過我更愿意將‘編譯期間’四個字改為‘運行之前’,否則容易讓人誤解為靜態(tài)類型語言一定是編譯型語言(Compiled Language)。”

          問號問道:“是否可以這么說:靜態(tài)類型語言需要變量申明,而動態(tài)類型語言則不需要?”

          “這話只對了一半。”冒號評論,“動態(tài)類型語言固然不需要顯式的變量申明(Explicit Declaration),一些靜態(tài)類型語言有時也不需要。典型的如ML、Haskell之類的函數(shù)式語言,編譯器可以通過上下文來進行類型推斷(type inference)。”

          嘆號感慨:“動態(tài)類型語言不必申明變量,甚至一個變量在不同地方可以代表不同類型,多省事多方便啊!”

          冒號微微頷首:“動態(tài)類型語言的確有簡明快捷的優(yōu)勢,并且天然具有泛型(generic)特征,代碼更加靈活。比如,動態(tài)類型有一種被稱作鴨子類型(Duck Typing)的形式。”

          逗號感到有趣:“鴨子類型?很滑稽的名字。”

          “這種類型的思想是:如果一個對象既會走鴨步又會呷呷叫,何妨將其視作鴨子呢?”冒號說著投影出一段Ruby代碼——


          class Duck                        #會叫會游的鴨
              def shout
                  puts 
          '呷呷呷'
              end
              def swim
                  puts 
          '鴨泳'
              end
          end

          class Frog                         #會叫會游的蛙
              def shout
                  puts 
          '呱呱呱'
              end
              def swim
                 puts 
          '蛙泳'
              end
          end

          def shoutAndSwim(duck)   #讓一只會叫會游的家伙邊叫邊游
              duck.shout
              duck.swim
          end

          shoutAndSwim(Duck.
          new)   #讓一只鴨邊叫邊游
          shoutAndSwim(Frog.
          new)    #讓一只蛙邊叫邊游


          冒號繼續(xù)講解:“在Smalltalk、Python和Ruby等動態(tài)類型的OO語言中,只要一個類型具有shout和swim的方法,它就可以為shoutAndSwim所接受。在Java這種靜態(tài)類型語言中是不可能的,除非鴨和蛙在同一繼承樹上,或者二者均顯式實現(xiàn)了一個包含shout和swim的公用接口。”

          句號敏銳地指出:“C++是靜態(tài)類型語言,但它的模板也可實現(xiàn)類似功能,并不需要引入繼承關系。”

          “非常正確!但請接著看下去。”冒號又放出一段投影——


          class Cock                                 #會叫不會游的雞
              def shout
                  puts 
          '喔喔喔'
              end
          end
           
          class Fish                               #會游不會叫的魚
              def swim
                  puts 
          '自由泳'
              end
          end

          def shoutOrSwim(duck, flag)   #讓一只會叫或會游的家伙叫或游
              flag 
          ? duck.shout : duck.swim
          end

          shoutOrSwim(Cock.
          newtrue)       #讓一只雞叫
          shoutOrSwim(Fish.
          newfalse)       #讓一只魚游

          “這里雞沒有swim的方法,魚沒有shout的方法。若采用C++的模板,shoutOrSwim是無法通過編譯的。但在支持Duck 類型的語言中,只要在運行期間不讓雞swim、讓魚shout——除非你突發(fā)奇想——一切平安無事。”冒號作了個OK的手勢。

          “動態(tài)類型語言真是越看越可愛。”嘆號簡直垂涎欲滴了。

          “Duck類型為軟件重用開啟了新的窗口,但凡事都是一分為二的。由于Duck類型的接口組合是隱性的,其使用者需要比普通Interface更小心以避免誤用;其維護者也需要更小心以避免破壞客戶代碼;此外它也可能造成濫用——將會叫會游的東西放進池塘似乎不算壞主意,但如果一艘輪船趁機也開了進來,恐怕就不那么美妙了。”

          眾皆莞爾。

          “再來看看靜態(tài)類型語言的好處:由于在運行之前進行了類型檢查,一方面代碼的可靠性增強,另一方面編譯器有可能藉此優(yōu)化機器代碼以提高運行效率,同時相比前者節(jié)省了運行期的類型檢查時間。此外,變量類型的聲明表明了編程者的意圖,有輔助文檔的功效。”冒號解釋著,“兩種類型的體制可以用兩種法律原則來類比:靜態(tài)類型檢查類似‘疑罪從有’的有罪推定制——在被證明合法之前是非法的,動態(tài)類型檢查類似‘疑罪從無’的無罪推定制——在被證明非法之前是合法的。至于如何取舍,套用一句話:‘Static Typing Where Possible, Dynamic Typing When Needed’。”

          問號提出新問題:“動態(tài)類型語言與弱類型語言有何不同?”

          冒號喟言:“它們也常常被混為一談,但類型的動靜與強弱完全是正交的兩個概念。前者以類型的綁定(binding)時間來劃分,后者以類型的剛性強度來劃分。通常弱類型語言Weakly-typed Language)允許一種類型的值隱性轉(zhuǎn)化為另一種類型,而強類型語言(Strongly-typed Language)則不允許。舉個例子,1"2"VB中等于3——第二個字符串轉(zhuǎn)化為整數(shù);在Javascript中等于"12"——第一個整數(shù)轉(zhuǎn)化為字符串;在C中則等于一個不定的整數(shù)值——第二個字符串作為地址來運算。這樣似乎很有趣很方便,但程序容易藏污納垢,滋生臭蟲(bug)。”

          引號想起:“好像還有一種所謂的類型安全語言?”

          逗號緊緊抱著頭,仿佛害怕裂開。

          “類型按安全性來劃分,可分為類型安全語言(Type-safe Language)和類型不安全語言(Type-unsafe Language)。一般認為強類型語言是類型安全的,弱類型語言是類型不安全的。”冒號應道,“至此,我們已論及數(shù)據(jù)類型的三種劃分方式。值得一提的是,這些劃分并非涇渭分明的,更多的是定性而非定量的描述,甚至沒有公認統(tǒng)一的定義。但了解它們,對我們?nèi)蘸蟮膶W習是有所裨益的。”

          posted on 2008-05-25 19:27 鄭暉 閱讀(2516) 評論(3)  編輯  收藏 所屬分類: 冒號和他的學生們

          評論

          # re: 冒號和他的學生們(連載15)——數(shù)據(jù)類型 2008-06-03 09:51 木東

          寫的非常好,謝謝博主!
          一直在關注這個系列
          提個小建議:

          情景式的教學確實確實很好,也很有創(chuàng)意,但是看的時候有時也會感覺啰嗦,煩
          能不能在每篇文章加個小結(jié),把文章的要點以及精辟的句子概括出來,這樣也能夠做到上下兼顧。  回復  更多評論   

          # re: 冒號和他的學生們(連載15)——數(shù)據(jù)類型 2008-06-03 11:43 鄭暉

          @木東
          您提的意見非常好!事實上原本的打算是將每篇分為“冒號課堂”與“句號筆記”兩部分,后者用于小結(jié)。但因?qū)崟r創(chuàng)作,暫無暇顧及后者,今后一定補上!歡迎多提意見!  回復  更多評論   

          # re: 冒號和他的學生們(連載15)——數(shù)據(jù)類型 2008-08-05 16:13 leweslove

          如果作者寫小結(jié),那是省了讀者寫小結(jié)了  回復  更多評論   

          導航

          統(tǒng)計

          公告

          博客搬家:http://blog.zhenghui.org
          《冒號課堂》一書于2009年10月上市,詳情請見
          冒號課堂

          留言簿(17)

          隨筆分類(61)

          隨筆檔案(61)

          文章分類(1)

          文章檔案(1)

          最新隨筆

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 连州市| 仁寿县| 伊金霍洛旗| 浠水县| 肥城市| 西峡县| 新建县| 湟源县| 和田市| 遵化市| 丰台区| 黑河市| 黑山县| 察哈| 阳东县| 江油市| 芜湖县| 巫溪县| 唐海县| 保德县| 绥滨县| 嵊泗县| 富宁县| 铁岭县| 昭觉县| 贵定县| 阿坝| 永顺县| 桂东县| 永新县| 仙居县| 台东市| 镇康县| 延安市| 双江| 梁山县| 宁强县| 长海县| 蓬安县| 剑川县| 徐闻县|