💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# **第 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 中的变量名和方法名使用前者,类名和模块名的使用后者。