# **第 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 ## 对局部变量赋值
~~~
![{%}](https://box.kancloud.cn/2015-10-26_562e01da0f287.png)
**图 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 参考集
- 后记
- 谢辞