天天看點

Ruby初探

入職後的第一個項目決定采用ROR開發。聽說ROR的大名已經很長時間了,正好借此機會系統學習一番。要系統學習一項新的技術,我習慣先從宏觀上了解它的特點、起源、發展過程、目前的狀況這些常識性的内容。這些常識性内容看起來就像看故事,比較輕松,算是一個熱身,主要關注兩個方面:第一就是這些常識性知識,這也許是将來和别人交流的鋪墊;第二就是了解這種技術中提倡的一些原則和方法論上的東西,這些是精髓,很多的技術細節都是圍繞這些原則設計和實作的,對于深入了解技術細節是很有幫助的。

ROR和Ruby的很多資料都是從網上搜集的,包括很多的電子書。學習ROR首先要學習Ruby語言,了解Ruby的前世今生。網上的一份較早的Ruby入門資料(張開川著)短小精煉,我覺得作為初步了解Ruby是很合适的,總結一些有助于學習和了解Ruby的内容于此,不過多涉及文法。

計算機程式設計語言的發展總是與飛速發展的世界息息相關,Ruby是為了适應變化,提高和完善程式設計藝術而出現的。Ruby是一種動态腳本語言,彙集了許多其他語言的特性,例如Perl的正規表達式、Python的簡單性可讀性、Smalltalk的純面向對象文法和單繼承、Lisp的無窮嵌套的文法、Java的線程。程式設計語言将向動态回歸,指令式語将與函數式語言融合。終究有一天,程式設計語言完全智能化,人們用自然語言來程式設計。而Ruby有可能在程式設計語言的智能化發展道路上起到承上啟下的作用(張開川)。Ruby靈巧、快速,但其實并不簡單。Ruby中實作一個小功能,可以有3種甚至4種完全不同的思路與方法,因為Ruby在文法層次實作了備援,但是這樣一來:1)程式員深入掌握 Ruby變得不很容易;2)程式員們互相讀懂代碼也很難;3)軟體生産是一種大規模地、群體合作的行為,而Ruby 靈巧,快速,千變萬化,沒有統一風格,難于解耦,不适合工業生産。(目前業界很多公司群組織都在積極實踐Ruby應用于大規模的軟體開發)。

學習程式設計的過程,就是深化了解變量的過程。先說變量是什麼?變量是代号。在數學中,你寫下一個小寫的英文字母“f”,這個f 可以是數字5,也可以一個函數式f = n * n + 1,還可以是一個曲面,或者是一個邏輯蘊涵關系。程式設計語言的産生,建立在數學的基礎上。在彙編語言的時代,一條語句”LD  x, 37H”,其中LD是操作碼,代表一種操作;x和37H是操作數,是被操作的對象。無論是LD,還是x和37H,對于機器來說,都隻是符号。後來,程式設計語言發展成兩大類,一類是函數式語言,一類是指令式語言。指令式語言将操作數演化成現在我們熟悉的變量,将操作碼演化成方法(或叫函數),對變量執行各種操作。面向對象程式設計又将基本變量和方法封裝在一起,成為一個更複雜的變量——對象。但是,在一個類中仍然區分基本變量和方法。函數式語言則不同,一開始的函數式語言不區分變量和方法,一切都是表(list),表就是能夠不斷分解成單個元素的數學符号。表可以是變量,可以是方法。後來的有些函數式語言,吸取了指令式語言的文法,也區分變量和方法。也有一些指令式語言,融合了函數式語言的文法,Ruby就是這樣的語言,變量和方法區分得不很明顯。

其次,說一說變量有哪些特征呢?

1)變量有名字;

2)變量代表的那個事物應該有一個可以用數學度量的值:長度,面積,速度,大小,磁場強度…

3)為了差別事物,我們将事物分成幾個基本類型。是以,為了代表不同類型的事物,變量也就有了不同的類型。

4)事物總是有産生到消亡的一個過程,是以,代表事物的變量也就有了生命周期。計算機科學,是一門将時間轉換成空間的科學。在程式中,我們把變量的生命周期,稱之為變量的作用域。

變量名,變量值,變量類型,變量的作用域,是學習指令式語言不可回避幾個要素。

如果你是一門程式設計語言的設計者,仔細考慮一下,上面四個要素,對于程式設計語的使用者都是必須的嗎?作為一個使用者,1)、2)是必須的。至于類型、生命期,與我何幹?某個變量,我使用一下就丢棄了,要我操心太多,還不如我從頭設計呢。由編譯核心(或解釋核心)在運作時刻來判斷變量類型的語言,叫動态類型語言。

變量既然是代号,那麼可以代表數字、文字(字元串)、代碼序列(塊、閉包)、一段程式(檔案)……在運作中,變量能夠随時代表不同的事物,而不管事物是什麼類型,這種語言,叫弱類型語言。這裡的“弱”,是指弱化了類型的概念,不理會類型的差異。

Ruby語言還是有基本類型的。至于變量作用域,純粹的函數式語言中是沒有這個概念的。Ruby中是有變量作用域概念的。Ruby語言中,一切都是對象,變量是不是對象呢?變量不是對象,變量隻是引用某個對象的時候,你看到的一個代号而已。

Ruby是動态類型語言,不用給任何變量指定資料類型,解釋器會在你第一次指派給變量時,在内部将資料類型記錄下來。Ruby語言中,一個變量被賦予了某個資料類型的值,在程式中你可以随時再予這個變量其它資料類型的值。相對于Java, Ruby對于變量的使用給予了使用者很大的自由。在Java中,程式設計的時候就完成了類型比對的檢測,這屬于前期綁定;Ruby是在運作中檢測,檢測類型比對嗎?不是檢測類型比對,而是檢測文法,隻要與文法定義不沖突就通過。Ruby是動态語言,可以改變Ruby程式的結構、功能,在Ruby程式運作中,方法、屬性可以被加入或去除,新的類或對象可以被建立,新的子產品可以出現。Ruby的動态類型特點是一把雙刃劍,熟手遊刃有餘,生手常常傷着自己。在沒有了編譯器查錯的日子裡,又沒有完全駕馭Ruby之前,如何避免常常出錯呢?一種辦法就是死盯住變量的命名。用一些有意義的名字,不必太長,但是應該少用單字元,除非是循環指針變量。你也許認為自己能看懂就行了,這是十分有害的想法。在一個項目組中,程式員是要彼此互相溝通合作的。當壞習養成後,要改是很難的。

Ruby語言隻有重寫(override),沒有其它語言具有的嚴格意義上的重載(overload)。下面仔細分析Ruby為何沒有重載,隻有重寫。Java的重載,參數清單有三種不同形式:1)參數個數不同;2)參數個數相同,參數類型不同;3)參數個數相同,參數類型相同,對應位置不同。Ruby支援預設參數,支援可變參數。由于預設參數和可變參數,參數個數不同而産生的重載,在 Ruby中不再有效。Ruby 語言中,定義方法時,不指定參數類型,是以第二種形式的重載也不存在。第三種形式的重載,實際是第二種形式的演化,是以,也就不存在了。綜上所述,Ruby語言沒有方法的重載。如果在同一個類中寫兩個同名方法呢?總是寫在後面的方法被執行。Java和 C++是靜态語言,程式代碼運作中不可以再改變類的屬性、方法,為了更好地表現面向對象的多态特征,是以用重寫和重載來加強程式的靈活性,在程式運作的時候,動态地選擇要使用的方法,完成後期綁定。而Ruby是動态語言,可以随時改變類的屬性、方法,是以重寫和重載的重要性就降低了。

在Ruby裡,可以給具體的執行個體對象添加執行個體方法,把這樣的方法稱之為單例方法。定義單例方法,首先要生成一個執行個體對象,其次,要在方法名前加上對象名和一個點号“.”。  執行個體方法,屬于類的每個執行個體對象。單例方法隻出現在單個執行個體對象中。用單例方法可以極大地豐富多态性在 Ruby中的表現力。

Ruby裡有一個很重要的單元是子產品。一般把功能相關的程式代碼放在一個子產品裡。子產品與類非常相似,但是子產品不可以有執行個體對象,子產品之間不存在繼承關系。子產品的作用有三:1)可以被其它程式代碼重複使用;2)提供了一個命名空間,防止命名沖突;3)實作了類似多重繼承的功能。通過“<  父類名 ”,一個子類可以得到父類的屬性和方法;通過“include 子產品名”,一個子類可以得到某個子產品的常量和方法。類不能被 include。 include方法為一個類的所有對象包含某個子產品;extend方法為一個類的某對象包含某個子產品。

使用 require 方法讓你的程式檔案變得簡潔有力。require方法包含另一個檔案,另一個檔案名需要是一個字元串。還有一個load方法與require方法相對應,也用來包含另一個檔案。require包含的檔案隻加載一次,遇到同一檔案時自動忽略;不同路徑下的同檔案會多次加載。load包含的檔案加載多次,即使是相同路徑下的同一檔案。利用load多次加載檔案的特性,可以用來實作程式的無縫更新和系統的熱部署。程式功能改變了,你隻需要重新load 一次,其它代碼與它再次互動的時候,實際上已經不是原來的程式了。require 加載檔案時可以不加字尾名,load 加載檔案時必須加字尾名。require 一般情況下用于加載庫檔案,而 load 用于加載配置檔案。