# **第 4 章 對象、變量和常量**
本章會介紹使用 Ruby 操作數據時需要掌握的基礎知識,主要有以下四部分內容。
-
**對象**
-
**類**
-
**變量**
-
**常量**
### **4.1 對象**
在 Ruby 中,表現數據的基本單位稱為對象(object)。
對象的類型非常多,我們這里只介紹一些常用的對象。
-
**數值對象**
`1`、`-10`、`3.1415` 等是表示數字的對象,另外還有表示矩陣、復數、素數、公式的對象。
-
**字符串對象**
`" 你好 "`、`"hello"` 等表示文字的對象。
-
**數組對象、散列對象**
表示多個數據的集合的對象。
-
**正則表達式對象**
表示匹配模式的對象。
-
**時間對象**
比如“2013 年 5 月 30 日早上 9 點”等表示時間的對象。
-
**文件對象**
一般我們可以理解為表示文件本身的對象,但確切來說,它是對文件進行讀寫操作的對象。
-
**符號對象**
表示用于識別方法等名稱的標簽的對象。
除此以外,Ruby 還有范圍對象(Range)、異常對象(Exception)等。
### **4.2 類**
Ruby 的類(class)表示的就是對象的種類。
對象擁有什么特性等,這些都是由類來決定的。到目前為止,我們介紹過的對象與其所屬類的對應關系如表 4.1 所示。
**表 4.1 對象與類的對象表**
<table border="1" data-line-num="47 48 49 50 51 52 53 54 55 56" width="90%"><thead><tr><th> <p class="表頭單元格">對象</p> </th> <th> <p class="表頭單元格">類</p> </th> </tr></thead><tbody><tr><td> <p class="表格單元格">數值</p> </td> <td> <p class="表格單元格"><code>Numeric</code></p> </td> </tr><tr><td> <p class="表格單元格">字符串</p> </td> <td> <p class="表格單元格"><code>String</code></p> </td> </tr><tr><td> <p class="表格單元格">數組</p> </td> <td> <p class="表格單元格"><code>Array</code></p> </td> </tr><tr><td> <p class="表格單元格">散列</p> </td> <td> <p class="表格單元格"><code>Hash</code></p> </td> </tr><tr><td> <p class="表格單元格">正則表達式</p> </td> <td> <p class="表格單元格"><code>Regexp</code></p> </td> </tr><tr><td> <p class="表格單元格">文件</p> </td> <td> <p class="表格單元格"><code>File</code></p> </td> </tr><tr><td> <p class="表格單元格">符號</p> </td> <td> <p class="表格單元格"><code>Symbol</code></p> </td> </tr></tbody></table>
> **備注**
> “×× 類的對象”,我們一般也會說成“×× 類的實例(Instance)”。所有 Ruby 對象其實都是某個類的實例,因此在 Ruby 中的對象和實例的意義幾乎是一樣的。
> 另外,我們在強調某個對象是屬于某個類時,經常會使用“實例”來代替“對象”。例如,我們會說“字符串對象 `"foo"` 是 `String` 類的實例”。
表 4.1 的類都是 Ruby 默認提供的,我們也可以按照實際需要自定義新的類。
類的相關內容,我們將會在第 8 章詳細說明。
### **4.3 變量**
在 1.9 節我們提到過,變量就像是對象的名片。
Ruby 中有四種類型的變量。
-
**局部變量(local variable)**
-
**全局變量(global variable)**
-
**實例變量(instance variable)**
-
**類變量(class variable)**
變量的命名方式決定了變量的種類。
-
**局部變量**
以英文字母或者 _ 開頭。
-
**全局變量**
以 $ 開頭。
-
**實例變量**
以 @ 開頭。
-
**類變量**
以 @@ 開頭。
除了以上四種類型以外,還有一種名為偽變量(pseudo variable)的特殊變量。1 偽變量是 Ruby 預先定義好的代表某特定值的特殊變量,因此即使我們在程序里給偽變量賦值,它的值也不會改變。Ruby 中,`nil`、`true`、`false`、`self` 等都是偽變量。它們表面上雖然看著像變量,但實際的行為又與變量有差別,因此稱為偽變量。
1實際上還有一種叫預定義變量(Pre-defined Variable)的特殊變量。——譯者注
### **局部變量與全局變量**
首先讓我了解一下什么是局部變量。
所謂局部,即變量在程序中的有效范圍(也稱為變量的作用域)是局部的。也就是說,在程序某個地方聲明的變量名,在其他地方也可以使用,程序會也會認為這兩個變量是沒有關系的。2
2局部變量也可稱為本地變量。——譯者注
與局部變量相對的是全局變量。只要全局變量的名稱相同,不管變量在程序的哪個部分使用,程序就認為是它們是同一個變量。
舉個例子,假設有個程序引用了其他程序作為自己的程序一部分。這時,如果原程序與被引用程序中,都有一個相同名稱的變量 `x`,由于 `x` 是局部變量,因此程序不會認為這兩個變量 `x` 是同一個變量。但是,如果是擁有相同名稱的全局變量 `$x`,則程序會認為這兩個變量 `$x` 是相同的變量。
代碼清單 4.1 和代碼清單 4.2 是調查變量作用域的兩個小程序。在 scopetest.rb 中,我們預先將變量 `$x` 和 `x` 都定義為 0 后,讀取 sub.rb 的內容。在 sub.rb 中,我們再把剛才兩個變量的值都設為 1。然后,回到 scopetest.rb 程序的第 6 行和第 7 行,我們輸出這兩個變量的值后會發現,`x` 的值沒有變化,但 `$x` 的值已經是 1 了。這是由于在 scopetest.rb 以及 sub.rb 中,程序會把 `$x` 當作同一個變量來處理,而把 `x` 當作不同的變量來處理。
**代碼清單 4.1 scopetest.rb**
~~~
1: $x = 0
2: x = 0
3:
4: require "./sub"
5:
6: p $x #=> 1
7: p x #=> 0
~~~
**代碼清單 4.2 sub.rb**
~~~
1: $x = 1 ## 對全局變量賦值
2: x = 1 ## 對局部變量賦值
~~~

**圖 4.1 局部變量與全局變量**
一般我們并不推薦使用全局變量。全局變量的值在程序的任何地方都可以修改,因此在規模較大的程序中使用時,會增加程序不必要的復雜度,給閱讀程序、修改程序造成意想不到的麻煩。本書也很少對全局變量進行說明,示例中也沒使用過。
程序首次給局部變量賦值的同時,該局部變量就被初始化了。如果引用了未初始化的局部變量,程序會拋出異常。
> **執行示例**
~~~
> irb --simple-prompt
>> x + 1
NameError: undefined local variable or method ` 1' for main:Object
from (irb):1
from /usr/local/bin/irb:16:in `<main>'
~~~
實例變量與類變量,是在定義類的時候用到的變量,因此我們留到第 8 章再詳細說明。
### **4.4 常量**
與變量類似的有常量(constant)。常量的作用和變量一樣,是某個對象的“名片”。不過與變量不同的是,對已經賦值的常量再進行賦值時,Ruby 會做出警告。
> **執行示例**
~~~
> irb --simple-prompt
>> TEST = 1
=> 1
>> TEST = 2
(irb):4: warning: already initialized constant TEST
(irb):3: warning: previous definition of TEST was here
=> 2
~~~
常量以大寫英文字母開頭。例如,Ruby 的運行版本(`RUBY_VERSION`)、運行平臺(`RUBY_PLATFORM`)、命令行參數數組(`ARGV`)等,都是 Ruby 預定義的好的常量。關于預定義常量,我們將會在 B.4.2 節介紹。
### **4.5 保留字**
表 4.2 中的單詞,在程序中作為名稱使用時會受到限制。這些受到限制的單詞,我們稱為保留字。在程序里,如果不小心使用了 `end`、`next` 等作為變量名,Ruby 會提示我們語法錯誤。
> **執行示例**
~~~
> irb --simple-prompt
>> end = 1
SyntaxError: (irb):8: syntax error, unexpected keyword_end
end = 1
^
from /usr/local/bin/irb:16:in `<main>'
~~~
**表 4.2 Ruby 的關鍵字一覽**
| `\_\_LINE\_\_` | `\_\_ENCODING\_\_` | `\_\_FILE\_\_` | `BEGIN` | `END` |
|-----|-----|-----|-----|-----|
| `alias` | `and` | `begin` | `break` | `case` |
| `class` | `def` | `defined?` | `do` | `else` |
| `elsif` | `end` | `ensure` | `false` | `for` |
| `if` | `in` | `module` | `next` | `nil` |
| `not` | `or` | `redo` | `rescue` | `retry` |
| `return` | `self` | `super` | `then` | `true` |
| `undef` | `unless` | `until` | `when` | `while` |
| `yield` | | | | |
### **4.6 多重賦值**
我們已經介紹過“變量`=`值”這樣的變量賦值方法,Ruby 還提供了一個只用一個表達式就能給多個變量賦值的簡便方法——多重賦值。很多情況下我們都會用到多重賦值,在這里舉幾個比較典型的例子供大家參考。
### **4.6.1 合并執行多個賦值操作**
有時我們希望把一組的變量同時賦值。
~~~
a = 1
b = 2
c = 3
~~~
像這樣的賦值語句,程序可以簡化為只有一行。
~~~
a, b, c = 1, 2, 3
~~~
這樣就能輕松地將 1、2、3 分別賦值給變量 a、b、c。如果對一組不相關的變量進行多重賦值,程序會變得難懂,因此建議對彼此相關變量進行多重賦值。
即使`=` 左右兩邊列表的數量不相等,Ruby 也不會報錯。左邊被賦值的變量的個數比較多時,Ruby 會自動將 nil 賦值給未分配值的變量。
~~~
a, b, c, d = 1, 2
p [a, b, c] #=> [1, 2, nil]
~~~
變量部分比較少時,Ruby 會忽略掉該值,不會分配多余的值。
~~~
a, b, c = 1, 2, 3, 4
p [a, b, c] #=> [1, 2, 3]
~~~
變量前加上`*`,表示 Ruby 會將未分配的值封裝為數組賦值給該變量。
~~~
a, b, *c = 1, 2, 3, 4, 5
p [a, b, c] #=> [1, 2, [3, 4, 5]]
a, * b, c = 1, 2, 3, 4, 5
p [a, b, c] #-> [1, [2, 3, 4], 5]
~~~
### **4.6.2 置換變量的值**
現在我們來考慮一下如何置換變量 `a`、`b` 的值。通常,我們需要一個臨時變量 `tmp` 暫時地保存變量的值。
~~~
a, b = 0, 1
tmp = a # 暫時保存變量a 的值
a = b # 將變量b 的值賦值給a
b = tmp # 將原本變量a 的值賦值給變量b
p [a, b] #=> [1, 0]
~~~
使用多重賦值,只需一行程序就搞定了。
~~~
a, b = 0, 1
a, b = b, a # 置換變量a、b 的值
p [a, b] #=> [1, 0
~~~
### **4.6.3 獲取數組的元素**
用數組賦值,左邊有多個變量時,Ruby 會自動獲取數組的元素進行多重賦值。
~~~
ary = [1, 2]
a, b = ary
p a #=> 1
p b #=> 2
~~~
只是希望獲取數組開頭的元素時,可以按照以下示例那樣做。左邊的變量列表以,結束,給人一種“是不是還沒寫完?”的感覺,建議盡量少用這樣的寫法。
~~~
ary = [1, 2]
a, = ary
p a #=> 1
~~~
### **4.6.4 獲取嵌套數組的元素**
我們來看看數組 `[1, [2, 3], 4]`,用之前介紹的方法,我們可以分別取出 `1`,`[2, 3]`、`4` 的值。
~~~
ary = [1, [2, 3], 4]
a, b, c = ary
p a #=> 1
p b #=> [2, 3]
p c #=> 4
~~~
像下面那樣把左邊的變量括起來后,就可以再進一步將內部數組的元素值取出來。
~~~
ary = [1, [2, 3], 4]
a, (b1, b2), c = ary # 對與數組結構相對應的變量賦值
p a #=> 1
p b1 #=> 2
p b2 #=> 3
p c #=> 4
~~~
只要等號左邊的變量的結構與數組的結構一致,即使再復雜的結構,多重賦值都可以輕松對應。
> **專欄**
> **變量的命名方法**
> 以變量名開頭來決定變量的種類,這是 Ruby 中對變量命名時唯一要堅決遵守的規則。雖然如此,但是根據以往的編程經驗,也有一些非強制性的、約定俗成的變量命名規則。在大多數情況下,遵循這些規則能使程序變得易于閱讀,對我們來說有百利而無一害。
> - > **不要過多使用省略的名稱**
> 有些編程語言會限制變量名的長度,但 Ruby 不需要在意變量名的長度。當然,過長的名稱是不便于閱讀的,但是與其起個不知所云的短的名稱,老老實實地為變量取個長點的好理解的名稱,對以后閱讀、理解程序是非常有幫助的。
> 但是,我們也還是有一些約定俗成的短名稱變量。進行數學、物理等計算時,根據計算對象的不同,很多情況下會使用短名稱的變量名,像坐標使用 `x`、`y`、`z`,速度使用 `v`、`w`,循環次數使用 `m`、`n` 等。另外,我們編寫程序時,也經常使用 `i`、`j`、`k` 等作為循環時需用到的變量名。
> - > **對于多個單詞組合的變量名,使用 _ 隔開各個單詞,或者單詞以大寫字母開頭**
> 也就是說,要么這樣叫做 `sort_list_by_name`,要么叫做 `sortListByName`。一般來講,Ruby 中的變量名和方法名使用前者,類名和模塊名的使用后者。
- 推薦序
- 譯者序
- 前言
- 本書的讀者對象
- 第 1 部分 Ruby 初體驗
- 第 1 章 Ruby 初探
- 第 2 章 便利的對象
- 第 3 章 創建命令
- 第 2 部分 Ruby 的基礎
- 第 4 章 對象、變量和常量
- 第 5 章 條件判斷
- 第 6 章 循環
- 第 7 章 方法
- 第 8 章 類和模塊
- 第 9 章 運算符
- 第 10 章 錯誤處理與異常
- 第 11 章 塊
- 第 3 部分 Ruby 的類
- 第 12 章 數值類
- 第 13 章 數組類
- 第 14 章 字符串類
- 第 15 章 散列類
- 第 16 章 正則表達式類
- 第 17 章 IO 類
- 第 18 章 File 類與 Dir 類
- 第 19 章 Encoding 類
- 第 20 章 Time 類與 Date 類
- 第 21 章 Proc 類
- 第 4 部分 動手制作工具
- 第 22 章 文本處理
- 第 23 章 檢索郵政編碼
- 附錄
- 附錄 A Ruby 運行環境的構建
- 附錄 B Ruby 參考集
- 后記
- 謝辭