天天看點

《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界

本節書摘來自異步社群《ruby程式員修煉之道》一書中的第1章,第1.1節進入ruby的世界,作者【美】david a. black(戴維 a. 布萊克),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

第1章 進入ruby的世界

ruby程式員修煉之道(第2版)

本章主要内容

ruby文法的生存工具箱①

ruby基礎程式設計指引:程式編寫、儲存、運作和錯誤檢查

ruby安裝指南

ruby的擴充機制

ruby中易用的指令行工具,包括互動式ruby解釋器(irb)

本書的内容是ruby基礎,而本章是基礎中的基石。本章的目标是讓讀者在開始學習ruby之前掌握足夠的知識和技巧。

接下來讀者将看到ruby的基本文法和技術,以及ruby的運作機制:如何寫一個程式,怎樣使用ruby運作程式,以及如何把一個程式分寫到多個檔案中。此外,讀者還将學習到一些開關(switch)②的用法,即它們如何改變ruby解釋器(名為ruby的程式,用于執行用ruby語言寫的程式檔案)的作用,也會學習使用一些重要的輔助工具,讓讀者的ruby程式員生涯更加輕松和高效。

本章将ruby領域的觀點分為以下3個基本層次。

語言核心:設計原則、文法、語義。

ruby支援的擴充(extension)和類庫(library),以及使用者自己添加到擴充中的工具。

ruby自帶的指令行工具,用于運作解釋器和其他一些重要工具。

這3個部分在一個系統中環環相扣,是以在本書中它們的内容會互相貫穿,不過在本章中會盡量分開讨論。盡管如此,這3個部分的内容都會貫穿在書的每一章中。

ruby,ruby,還是ruby?!

ruby是一門程式設計語言。我們會談論“學習ruby”,還會問一些問題,比如“你知道ruby嗎?”ruby是指一個計算機程式,特指ruby的解釋器,它可以讀取并運作程式。可以看到這個命名方式在一些文章中出現,如“你用ruby運作我的檔案,但什麼也沒發生。”,或者“ruby可執行檔案的完全路徑是什麼?”最後是ruby,準确來說,沒有這樣的寫法。ruby不是一個縮略語詞彙,所有字母都大寫的拼寫方式向來都是錯的。人們在對待perl語言的時候也出現過同樣的錯誤,或許是因為他們看到了如basic和cobol這樣的拼寫方式,但ruby不同。一般用“ruby”來表示程式設計語言,用“ruby”來表示ruby的解釋器。

第1章不是隻為服務于其他章節而存在的,它也有自己存在的價值:學習真正的ruby技術和這門語言設計中的要點。其目标是為了引導和啟發讀者,但即使如此,學習過程還是會深入ruby語言的一些關鍵層面。

1.1 ruby語言基礎知識

本節的目标是讓讀者開始接觸ruby。這裡采用廣度優先的方法:大緻圍繞文法學習、代碼編寫、程式運作這樣一個循環過程展開。

說到這裡,讀者需要在自己的電腦上安裝好ruby。書中的例子都使用ruby 2.1.0③編寫。還需要準備一個文本編輯器(任何偏好的編輯器都可以,也可以用純文字編輯器,但文字處理軟體不行)和一個存放ruby程式檔案的目錄(亦稱檔案夾)。可以命名目錄為rubycode或者rubysamples,無論什麼名字都可以,隻要能夠差別于其他工作區并友善地找到練習程式檔案即可。

互動式ruby控制台程式(irb)是最好的朋友

随ruby釋出的irb實用工具,是一個ruby指令行工具,irb使用的廣泛程度高于ruby解釋器本身。啟動irb之後,鍵入ruby代碼,它将執行代碼并輸出結果值。

在指令行中鍵入irb,然後輸入在文中所見的示例代碼。例如:

100 + 32

=> 132

打開irb會話意味着可以随時測試任意數量的ruby代碼段。大多數ruby程式員都發現irb是不可缺少的,而且本章中的一些示例也是使用irb運作的。

讀者将看到下面irb的示例使用了指令行選項,它的輸出結果更易于閱讀。

irb --simple-prompt

運作irb時加入此選項或者不加,就可以看到--simple-prompt選項的效果。正如所見,這個簡單的提示符選項将使螢幕更簡潔明了。預設的(非簡單的)提示符顯示了更多的資訊,如在互動會話時的行号統計。但從衆多的示例可以看出,簡單的提示符已足夠使用。

因為irb是一個用于運作ruby語言的指令行工具,是以直到1.4.2節才會詳細讨論它。如有需要,可以轉到那一節去看一下,這是最直接的方式。

完成ruby的安裝,并建立好工作目錄的,就繼續學習ruby吧,這樣我們就能分享ruby學習和探索中的見地。掌握ruby的文法将會是一個良好的開端。

1.1.1 ruby文法生存包

下面3個表總結了ruby的一些特性,這對于了解本章的例子和開始體驗ruby語言大有益處。不必記住它們,隻要看一下并在稍後用到的時候回查即可。

表1-1包含了ruby的一些基本運算。表1-2中介紹了擷取基礎鍵盤輸入、将輸出發送到螢幕以及基本的條件語句。表1-3簡要描述了ruby的特殊對象和注釋的文法。

《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界
《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界
《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界
《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界

以上摘要表中已經包含了許多ruby的基礎和文法。讀者需要能夠辨認出ruby辨別符(identifier)的幾種不同寫法,尤其是對ruby中的對象和方法調用有一個感性認識。我們稍後将會談論這些内容。

1.1.2 多種多樣的ruby辨別符

ruby的辨別符類型很少,一眼就能辨認和區分它們。辨別符體系如下。

變量(variable):

局部變量(local variable);

執行個體變量(instance variable);

類變量(class variable);

全局變量(global variable)。

常量(constant);

關鍵字(keyword);

方法名(method name)。

這是一個很小的體系,很容易掌握,這一節将讨論它們。記住本節的目标是學習辨認不同的辨別符。本書後面将學習如何使用和使用它們的時機。這隻是辨別符知識的第一課。

1.變量

局部變量以小寫字母或者下劃線開頭,包含字母、下劃線或數字。x、string、abc、start_value和firstname都是有效的局部變量命名方式。然而,值得注意的是,在組合多個單詞以命名局部變量時,ruby的約定是使用下劃線作為命名規範,而不使用駝峰命名法,如使用first_name而不使用firstname。

執行個體變量為獨立的對象存儲資訊,它通常以一個單獨的符号(@)開頭,後面的字元使用與局部變量相同的命名規則,如@age和@last_name。盡管局部變量不能以大寫字母開頭,但是執行個體變量可以在@符号之後的第一個位置使用大寫字母(但不能使用數字)。但是通常來說,@符号之後還是使用小寫字母。

類變量在每一個類層級上存儲資訊(同樣,現在也不用擔心它的語義)。它與執行個體變量使用相同的命名規則,隻有一點不同,它以兩個@符号(@@)開頭,如@@running_total。

全局變量可以通過它的美元引導符号($)辨認出來,如$population。跟在$符号之後的語句不使用局部變量的命名規則。有一些全局變量名為$:、$1和$/,還有$stdin和$load_path。但隻要以$符号開頭,它就是一個全局變量。這些非字母的辨別符是預定義的,是以不必擔心其中的标點符号是否合法。

表1-4總結了ruby變量的指令規範。

《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界

2.常量

常量使用大寫字母開頭。a、string、firstname、stdin都是有效的常量命名。在ruby命名規範中,如遇到命名多詞組合的常量時,可以使用駝峰命名法(firstname)也可以使用下劃線分隔且所有字母大寫(first_name)的方式。

3.關鍵字

ruby擁有很多的關鍵字,它們是預定義的保留詞,與特定程式設計任務和上下文關聯。關鍵字包括def(用于方法定義)、class(用于類定義)、if(條件執行)和_file_(目前被執行檔案的名稱)。關鍵字大約有40個,通常是簡短的、單一詞彙(與下劃線組合多單詞方式相反)的辨別符。

4.方法名

ruby中的方法命名遵從與局部變量相同的規則和約定(除了以?、!或=結尾的符号,其重要作用稍後會講述)。這是一種設計理念:方法并不因其自身作為方法而被人關注,而是簡單地作為提供值的表達式融入到程式的結構中。在一些上下文中,很難一眼就區分出一個表達式是一個局部變量還是一個方法名,這一切源自設計,是有意為之。

講完方法,現在已經有了一張ruby辨別符的“路線圖”,讓我們談一下程式設計語言的語義,尤其是對象及其方法的重要作用。

1.1.3 方法調用、消息和ruby對象

ruby把所有的資料結構和值都看做對象,從整數和字元串這樣簡單的标量(原子的)值,到數組(array)這樣的複雜的資料結構一概如此。每個對象都能響應一組特定的消息,對象能夠接收的每個消息直接對應一個方法——有名稱的、可以被有執行能力的對象觸發的可執行例程。

對象也可以用字面量構造器表示,如字元串用雙引号,或者已綁定值的變量。消息通過特殊的點運算符(.)送達:點右邊的消息被發送到點左邊的對象上。(另外,有許多特殊的給對象發送消息的方式,但是點是最常用和基礎的方式。)參見表1-1中的示例:

x = "100".to_i

點(.)意味着消息to_i被發送給字元串對象"100"。字元串對象"100"作為消息的接收者被調用。也可以說是方法to_i被字元串對象"100"調用。方法調用的結果是生成整數100,然後作為指派運算的右表達式指派給變量x。

為什麼使用兩種術語?

何苦為該說“發送to_i消息”還是說“調用to_i方法”而煩惱?對于同一操作為什麼有兩種不同描述?因為它們不完全相同。大多數時候,發送消息給接收的對象,對象就會執行對應的方法。但有些時候是沒有對應方法的,對于點右邊的任意辨別符,并不能確定接收者擁有的方法與發送的消息相比對。

這聽起來有些混亂,其實不然。因為對象可以攔截未知的消息并使它們擁有具體含義。例如,ruby on rails web開發架構大量使用了如下的技術:發送未知消息到對象并攔截那些消息,然後能夠在使用目前資料庫表的列名作為動态條件的情況下順暢運作。

方法可以帶有參數,這個參數同時也是對象。(雖然有些用于建立和操作對象的文法結構本身不是對象,但在ruby中幾乎所有一切都是對象。)下面是一個帶有參數的方法調用:

x = "100".to_i(9)

字元串對象100調用方法to_i并傳遞參數9,生成一個九進制的100所得到的十進制整數,是以x現在等于十進制的81。

這個例子也同時展示了使用圓括号包含參數的方式。這些圓括号通常情況下是可選的,但是在大多數複雜的情況下,為了避免文法上的歧義,圓括号的使用是必要的。大部分程式員都盡可能在方法調用時使用圓括号,這樣做是為了避免歧義。

完整的ruby程式是由對象以及發送給對象的消息所組成。作為一個ruby程式員,大多數時間要麼是定義對象所能完成的任務(定義方法),要麼是請求對象完成這些任務(給對象發送消息)。

接下來在書中将會深入地講述這些内容。再說明一下,這一段簡短的概述是引領讀者進入ruby世界的其中一步。當看到點出現在某些令人費解的位置時,應該把它了解為一個發送給對象(左邊)的消息(右邊)。同時,也該記住一些裸詞(bareword)風格的方法調用方式,例如puts在如下示例中的調用方式:

puts "hello."

這裡盡管缺少消息發送所需要的點以及該消息的顯式接收者,卻依然發送了消息puts并傳遞了參數"hello."給一個對象:預設對象self。在程式運作期間,雖然作為self的對象會通過特定規則發生改變,但self總是被預定義好的。在第5章中将會對self進行詳細闡述。現在,隻要知道像puts這樣的裸詞的方法調用方式即可。

對象的概念在ruby中是最為重要的,與此緊密相關并扮演重要角色的概念是類(class)。

用類解釋對象的由來

類定義了一組行為和功能,每一個對象是一個具體類的執行個體。ruby提供了大量的内置類,它們代表了重要的功能資料類型(如string、array、fixnum)。每次建立一個字元串對象的同時,就建立了一個string類的執行個體。

使用者可以編寫自己的類,甚至可以修改已經存在的ruby類。如果不喜歡字元串或者數組的行為,可以修改它。這看起來雖然不好,但是在ruby中是允許這樣做的。(第13章中将描述修改内置類的利弊。)

盡管每一個ruby對象都是類的一個執行個體,但是類的概念卻不如對象的概念那麼重要。那是因為對象可以發生改變,它可以獲得在類中沒有定義過的方法和行為。類負責将對象變為實際的存在,這就是耳熟能詳的執行個體化(instantiation)過程。而對象在執行個體化之後,就進入了自己的生命周期。

對象有能力包含一個在類中沒有定義的行為,這是設計ruby作為一門程式設計語言時最為核心的原則之一。正如讀者所猜測,書中将在不同的上下文中不斷地回到這一點來讨論。對目前而言,隻要意識到盡管每個對象對應一個類,但對象的行為不由對象的類唯一決定即可。

了解了ruby的知識(疑惑之時回看一下這些内容)後,讓我們嘗試着運作一個程式吧。

1.1.4 編寫和儲存一個簡單程式

在本節中,将會在之前建立的ruby示例程式目錄中建立一個程式檔案。第一個程式是一個攝氏—華氏度轉換工具。

**注意

當然,在真實場景中,溫度的轉換使用的是浮點數。而這裡在輸入輸出時使用整數,主要是為了專注于程式的結構和程式的執行。

這個例子将被反複提到,并根據本書的需要進行添加和修改。它遵循如下的疊代過程。**

整理程式的輸出結果。

從使用者的鍵盤輸入中接收輸入資料。

從檔案中讀取數值。

将程式結果寫入一個檔案。

第1個版本很簡單,僅關注檔案建立和程式運作的過程,而不用深入程式邏輯的細節。

建立第一個程式檔案

使用一個純文字編輯器,輸入代碼清單1-1所示的代碼到一個文本檔案并儲存為c2f.rb到示例目錄中。

《Ruby程式員修煉之道》(第2版)—第1章1.1節進入Ruby的世界

注意:根據使用者的作業系統的不同,ruby程式檔案有可能僅用檔案名或一個短名稱就可以獨立運作,并不需要使用檔案擴充名。盡管如此,請記住,.rb的檔案擴充名在一些情況下是強制的,這主要是涉及擁有多個檔案(後面會詳述)的程式和有檔案間互相查找機制的程式。在本書中,所有ruby程式檔案名都以.rb結尾,這是為了確定示例程式可以在盡可能多的平台上運作,同時盡可能少地對系統進行管理幹預。

現在,已經有了一個完整的(雖然很小)ruby程式,可以運作它了。

1.1.5 給ruby提供程式

運作一個ruby程式需要給ruby解釋器傳遞程式的源碼檔案(或者多個檔案),這個ruby解釋器名為ruby,後面會依次解釋。在提供一個程式給ruby而不是請求ruby運作程式之後,它會檢查程式代碼的文法錯誤。

1.檢查文法錯誤

如果在轉換公式中使用31替換32,就會發生一個程式性錯誤。ruby還是會适時地運作程式并給出有缺陷的結果。但是假如程式的第2行中不小心遺漏了右圓括号,那就是文法錯誤,ruby将不能運作這個程式。

$ ruby broken_c2f.rb

broken_c2f.rb:5: syntax error, unexpected end-of-input, expecting ')'

(錯誤出現在第5行,即程式的最後一行,因為ruby一直在耐心等候右圓括号出現,結果卻沒有。)

ruby 解釋器提供一種便捷的方式來檢查文法錯誤而不必運作程式。它會讀取檔案并指出文法是否有錯。為了檢查源檔案的文法錯誤,可以這樣做:

$ ruby -cw c2f.rb

指令行中的-cw标志(flag)是兩個标志的簡寫,它們分别是:-c和-w。标志-c意味着檢查文法錯誤。标志-w可以激活進階别的警告:如果程式都合乎ruby文法,ruby就不會發出警告,除非有比文法更值得商榷的理由。

假如輸入的檔案正确,将在螢幕上會看到如下資訊:

syntax ok

2.運作程式

為了運作程式,再次提供源檔案給解釋器,但是這一次不用加入-c和-w标志。

$ ruby c2f.rb

如果一切順利,将可以看到計算的結果輸出如下:

the result is

212

.

計算的結果正确,但是計算的結果超過了3行,這看起來不夠好。

3.溫度轉換器的第二次疊代

問題要追溯到puts指令和print指令的差別。假如字元串沒有以一個已經存在的換行符結束,puts指令會在它列印的字元串尾部插入新換行符。相反,print列印字元串之後就停止了,它不會自動跳轉到下一行。

為了修正這個問題,把前兩行的puts指令改為print:

(注意,is後面的空格,它是為了確定在is和數值之間有一個空格。)現在輸出的是:

the result is 212.

puts是“put(就是print)string”的縮寫。盡管put沒有直覺的表示會調用換行符,但是puts會這樣做:如同print,列印使用者的資料,之後自動地轉到新一行。假如讓puts列印已經以換行符結束的一行,它不會再次添加換行符。

假如讀者已經在其他程式設計語言中使用過列印的工具,而這些工具沒有自動添加換行符(如perl語言的print函數),那麼可以自己編寫類似ruby中的實作,一個能列印值并添加換行符的工具:

print fahrenheit, "n"

盡管如此,大可不必這樣去做,puts已經實作了。習慣使用puts吧,并在這個過程中遵循使用其他的ruby習語和約定。

**警告

在一些作業系統平台中(尤其是在windows中),程式運作的結尾會列印輸出額外的換行符。這意味着實際上print已經代替了puts,而puts很難被系統檢測到。意識到這兩者的差別,并在最常用的場景中使用其中一個,可以充分确信得到所期望的結果。**

看一下螢幕的輸出,接下來将擴充一點i/o領域的知識,包括鍵盤的輸入和檔案的操作。

1.1.6 鍵盤和檔案i/o

ruby提供了很多在程式執行過程中讀取資料的技術,包括從鍵盤讀取和從磁盤檔案讀取。它們有許多用途:不僅僅在編寫每個應用程式的過程中會用到,在編寫維護、轉換、管理或者操縱使用者的工作環境的代碼時也幾乎一定會用到。本章中包括了一些輸入處理的技術,更多關于i/o操作的技術詳見第12章。

1.鍵盤輸入

反複執行100攝氏度等于212華氏度的程式,其價值是有限的,更有價值的程式則是可以自由指定華氏溫度并獲得對應的攝氏溫度。

修改程式完成如上的功能需要幾個步驟,分别需要使用表1-1和表1-2中提及的方法gets(擷取鍵盤輸入的一行資料)和to_i(轉換為整型),讀者應該已經熟悉它們其中的一個。由于這不僅僅是修訂版本而是一個新程式,是以把代碼清單1-2中的代碼版本放到名為c2fi.rb的新檔案中(i意味着互動)。

代碼清單1-2 互動式溫度轉換器(c2fi.rb)

下面為新程式的一組運作結果:

将代碼清單1-2中代碼的輸入、計算、輸出操作進行簡化。簡化重寫後如下:

雖然這個版本确實減少了變量使用,但是會要求閱讀代碼的人接受這組有些密集(但是簡短)的表達式。任何既定的程式通常都需要在長代碼(也許更清晰)和短代碼(也許有點晦澀)之間進行抉擇。而有時候,短代碼則更為清晰,這就是ruby的編碼風格。

如果不深究更多細節,現在這個版本已經是一個通用的攝氏度轉華氏度的解決方案。接下來,讓我們學習檔案的讀取。

2.讀取檔案

從ruby程式中讀取檔案并不困難,至少在大多數情況下,比鍵盤輸入還要容易。溫度轉換器的下一個版本将從一個檔案中讀取一個數組,然後從攝氏度轉換為華氏度。

首先,建立一個檔案并命名為temp.dat(溫度資料),同時包含一個數字:

100

現在,建立第三個程式,命名為c2fin.rb(in意為檔案輸入),如代碼清單1-3所示。

代碼清單1-3 使用檔案輸入的溫度轉換器(c2fin.rb)

自然地,如果在檔案中改變數字,結果将會不同。那麼,怎樣将計算的結果寫入檔案中呢?

3.寫入檔案

從最簡單的操作來說,檔案寫入要比檔案讀取複雜一些。正如代碼清單1-4所示,執行寫入檔案的操作時,主要的額外步驟是要指定檔案的模式,在這個例子中是w(意為寫入)。儲存代碼清單1-4所示的這個版本,命名為c2fout.rb并運作它。

代碼清單1-4 使用檔案輸入的溫度轉換器(c2fout.rb)

調用方法 fh.puts fahrenheit的作用,是将 fahrenheit的值輸出到由fh對象進行寫入處理的檔案中。如果檢查檔案temp.out,無論輸入什麼數字,都可以看到轉換好的華氏溫度值。

作為練習,可以嘗試着把前面的例子進行合并,讓它從一個檔案讀取數字,轉換為華氏度,之後把結果寫入不同的檔案中。在适時引入一些ruby文法的同時,下一節會檢驗ruby的安裝,然後很快還會依次看到ruby如何管理擴充和庫。