ruby培训

 Ruby 语言
Grant Ren
hyqryq@gmail.com
2006 年11 月24 日
2
前 言
3
目 录
第一部分 Rub y 语言基础 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2
第一章 Rub y 语言概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2
§1.1 Ruby 的历史 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
§1.2 Ruby 名字的由来 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
§1.3 Ruby 的特点 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
§1.4 Ruby 和 Python 的比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
第二章 Rub y 编程环境 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 5
§2.1 Ruby 的安装 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
§2.1.1 在 Windows 95/98/Me/XP 上安装 Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
§2.1.2 在 Linux 上安装 Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
§2.2 运行 Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
§2.2.1 使用 Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
§2.2.2 使用 FreeRIDE 和 SciTE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
§2.2.3 使用 fxri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
§2.3 Rubyirb
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
§2.4 Rubyri
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
§2.5 RubyGems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
第 3 章类与对象 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 6
§3.1 类的定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
§3.2 对象,属性和方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4
§3.3 继承 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
§3.4 特殊方法与特殊类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
§3.5 类变量与类方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
§3.4 存取控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
§3.6 元类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
§3.7 Ruby 的动态性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
§3.8 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
§3.8.1 局部变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
§3.8.2 实例变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
§3.8.3 类变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
§3.8.4 全局变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
§3.8.5 常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
§3.8 与定义有关的操作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
§3.8.1 alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
§3.8.2 undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
§3.8.3 defined? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
第四章 基本类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4
§4.1 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
§4.2 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
§4.3 Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
§4.4 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
§4.5 Range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
§4.6 Symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
§4.7 正则表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
第五章 代码块和迭代器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4
§5.1 代码块 (Block) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5
§5.1.1 什么是代码块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
§5.1.2 代码块与对象 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
§5.2 迭代器 (Iterator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
§5.2.1 什么是迭代器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
§5.2.2 使用迭代器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
§5.2.3 yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
§5.2.4 编写自己的迭代器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
第六章 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1
§6.1 运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
§6.2 命令替换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
§6.3 赋值运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
§6.4 并行赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
§6.5 嵌套赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
§6.6 其他赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
§6.7 条件运算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
§6.8 case 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
§6.9 循环 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
§6.9.1 Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
§6.9.2 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
§6.9.3 Until . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
§6.9.4 Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
§6.9.5 For..In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
§6.9.6 Break , Redo , Next . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
§6.9.6.1 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
§6.9.6.2 redo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
§6.9.6.3 next . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
§6.9.7 Retry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6
第七章 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1
§7.1 运算符重定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
§7.2 变长参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
§7.3 块调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
§7.4 方法返回值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
第八章 模块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9
§8.1 名字空间 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
§8.2 mixin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
§8.3 使用 mixin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
§8.3.1 Comparable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
§8.3.2 Enumerable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
§8.3.3 Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
§8.4 Require, load 和 include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
第 9 章异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 8
§9.1 异常处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
§9.2 定义异常类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
§9.3 catch 和 throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
第 1 0 章多任务处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 7
§10.1 多线程处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
§10.1.1 线程创建 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
§10.1.2 线程操作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
§10.1.3 线程和异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
§10.1.4 线程调度 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
§10.1.5 线程同步 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7
§10.1.5.2 Monitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
§10.1.5.2 Mutex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
§10.1.5.3 Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
§10.2 多进程处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
§10.2.1 进程创建 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
§10.2.1.1 system 方法和反引号 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
§10.2.1.2 popen 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
§10.2.1.3 fork 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
第 1 1 章基本 I/O 操作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 7
§11.1 使用 Kernel 模块处理 I/O 操作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
§11.2 文件处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
§11.3 StringIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
§11.4 Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
第十二章 反射和对象空间 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2
§12.1 ObjectSpace 模块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
§12.2 察看类和对象的状态 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
§12.3 动态方法调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
§12.3.1 使用 send 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
§12.3.2 使用 Method 类和 UnboundMethod 类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
§12.3.3 使用 eval 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
§12.3.4 性能 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
§12.4 Hook 和回调方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
§12.4.1 什么是 Hook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
§12.4.2 Ruby 中的 Hook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8
§11.4.2 回调方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
§12.5 跟踪程序的运行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
§12.5.1 set_trace_func . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
§12.5.2 trace_var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
§12.5.3 caller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
§12.5.3 __FILE__,__LINE__ 和 SCRIPT_LINES__ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
第十三章 序列化和 YAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 9
§13.1 序列化的概念 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
§13.2 使用序列化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
§13.2.1 二进制数据保存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
§13.2.2 YAML 数据保存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
§13.3 定制序列化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
§13.3.1 二进制数据保存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
§13.3.2 YAML 数据保存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
§13.3 YAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
§13.3.1 集合类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
§13.3.1.1 序列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
§13.3.1.2 表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
§13.3.2 单行集合类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
§13.3.3 基本类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
§13.3.4 块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
§13.3.5 别名和锚( Aliases and Anchors ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
§13.3.6 文档 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
§13.3.7 Ruby 中 YAML 的使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
9
第十四章 安全控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2
§14.1 0 级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
§14.1 1 级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
§14.2 2 级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
§14.3 3 级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
§14.4 4 级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
第十五章 单元测试 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 5
§15.1 什么是单元测试 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
§15.2 Ruby 单元测试框架 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
第二部分 内置类与模块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 5
第一章 内置类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 6
§1.1 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
§1.2 Bignum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
§1.3 Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
§1.4 Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
§1.5 Continuation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
§1.6 Dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.7 Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.8 FalseClass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.9 File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.10 File::Stat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.11 Fixnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.12 Float . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.13 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.14 Integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
§1.15 IO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.16 MatchData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.17 Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.18 Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.19 NilClass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.20 Numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.21 Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.22 Proc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
10
§1.23 Process::Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
§1.24 Range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.25 Regexp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.26 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.27 Struct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.28 Struct::Tms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.29 Symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.30 Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.31 ThreadGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.32 Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
§1.33 TrueClass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
§1.34 UnboundMethod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
第二章 内置模块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3
§2.1 Comparable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.2 Enumerable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.3 Error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.4 FileTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.5 GC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.6 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.7 Marshal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.8 Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
§2.9 ObjectSpace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
§2.10 Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
§2.11 Process :: GID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
§2.12 Process :: Sys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
§2.13 Process :: UID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
§2.14 Signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
第三部分 Rub y 语言总结 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 6
附录 19 1
§1 术语对照 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
11
第一部分 Ruby语言基础
第一章 Ruby语言概述
§1.1 Ruby的历史
Ruby 语言的发明人是日本人松本行弘(Matsumoto Yukihiro),大家亲切的称呼
他"Matz"。
可能会出乎大家的意料,Ruby并不是一种近年来才诞生的语言,它的历史可以追溯到
1993年,Ruby之父Matz开始对脚本语言感兴趣。在通过一些分析和思考之后,Matz认为
脚本语言是可以变得很强大和灵活的,于是他准备把脚本语言作为他的发展方向。 和很多
人一样,Matz是一个面向对象程序设计的fans,自然而然他想研究一种支持面向对象程序
设计的脚本语言。随后的一段时间,他到网络上搜集了一些相关的资料,并且发现了Perl
5,当时Perl 5还没有发布。通过一段时间了解后,Matz.发现Perl 5这并不是他想的东西,
所以他放弃了把Perl 当作一个面向对象的脚本语言使用的念头。随后Matz 转向了
Python,Python是一个解释型的、面向对象语言,但是Matz发现Python并不能完全算作“
12
面向对象”语言。Matz 认为Python 是面向对象和过程化程序设计语言(Procedural
Programming Language)的混合产物。Matz希望找到的是一种比Perl更强大、比Python更面
向对象的语言,但是很遗憾, 这样的语言当时在地球上并不存在。于是Matz打算自己设计
一个全新的编程语言。1993年2月24日是一个值得纪念的日子,在这一天Ruby诞生了。
1995年12月Matz推出了Ruby的第一个版本Ruby 0.95。 在1996年以前,都是Matz.一个
人在开发进行Ruby的开发。后来随着Ruby社区的渐渐形成,很多社区成员给了Matz许多
有意义的帮助,包括提交bug和patch等。现在,Ruby像其他开源项目一样,有自己的开
发团队,任何有能力的个人或团体都可以参与Ruby的开发与进化。
§1.2 Ruby名字的由来
首先明确一点,Ruby并不是其他单词的缩写。受Perl的影响,Matz也想用一种宝石来
命名他的新语言,他使用了他的一位同事的生肖石-红宝石。后来,Matz意识到Ruby这
个名字十分恰当,首先,在生肖石中,Pearl代表六月,而Ruby代表七月。在字体大小上,
Pearl大小是5pt, ruby的大小是5.5pt。所以Ruby这个名字对于一种Perl的后续语言十分合
适。
§1.3 Ruby的特点
Ruby是一种功能强大的面向对象的脚本语言,可以使用它方便快捷地进行面向对象
程序设计。与Perl类似,而且Ruby具有强大的文本处理功能,使文本处理变得简单。此外
13
还可以方便地使用C语言来扩展Ruby的功能。
若您曾经“想要一种简单的面向对象的语言”,或者认为“Perl的功能虽然好用,但
它的语法真让人受不了”,又或者觉得“LISP系列语言的思想不错,但到处都是括号真
让人讨厌,最起码算式应该按照通常的样式书写”。那么,Ruby或许能让您满意。
归纳起来,Ruby有以下优点:
 解释型执行,方便快捷
Ruby是解释型语言,其程序无需编译即可执行。
 语法简单、优雅
语法比较简单,类似Algol系语法。
 完全面向对象
Ruby从一开始就被设计成纯粹的面向对象语言,因此所有东西都是对象,例如整数等基
本数据类型。
 内置正则式引擎,适合文本处理
Ruby支持功能强大的字符串操作和正则表达式检索功能,可以方便的对字符串进行处理。
 自动垃圾收集
具有垃圾回收(Garbage Collect,GC)功能,能自动回收不再使用的对象。不需要用户对
内存进行管理。
 跨平台和高度可移植性
Ruby支持多种平台,在Windows, Unix, Linux, MacOS上都可以运行。Ruby程序的
可移植性非常好,绝大多数程序可以不加修改的在各种平台上加以运行。
 有优雅、完善的异常处理机制
14
Ruby提供了一整套异常处理机制,可以方便优雅地处理代码处理出错的情况。
 拥有很多高级特性
Ruby拥有很多高级特性,例如操作符重载、Mix-ins、特殊方法等等,是用这些
特性可以方便地完成各种强大的功能。
同时,由于是解释型语言,Ruby也有下列缺点:
 解释型语言,所以速度较慢
 静态检查比较少
§1.4 Ruby和Python的比较
Python是Ruby的劲敌。其功力深厚,可谓“千年蛇妖”。但matz认为Python的功能仍
不完美,不然就不会创造Ruby了。
第二章 Ruby编程环境
§2.1 Ruby的安装
Ruby支持多种平台,包括Windows、Linux、各种类UNIX、MacOS X等。
15
§2.1.1 在Windows 95/98/Me/XP上安装Ruby
对于使用Windows平台的用户,安装Ruby是相当简单直接的事情。最方便的方法是使
用“OneClick
Ruby Installer”。
不知你有没有听说过SourceForge?SourceForge 是全球最大的开放源代码软件开发平
台和仓库。它集成了很多开放源代码应用程序,为软件开发提供了整套生命周期服务。在
Ruby 世界,也有一个类似的网站,那就是Rubyforge。“OneClick
Ruby Installer”是
Rubyforge上的一个开源项目,也是Rubyforge上下载量最大的项目之一。这个项目将Ruby
语言核心和一系列常用扩展集成到了一起,还包含支持Ruby的免费的IDE工具FreeRIDE
和SciTE,除了这些之外还包括帮助文档,示例代码,RubyGems包管理器,Fox GUI库,
fxri(Interactive Ruby Help & Console)等。和正如它名字所示,使用它,Ruby安装变得前
所未见的容易。你可以在下面的地址下载到它的最新版本:
http://rubyforge.org/projects/rubyinstaller/
§2.1.2 在Linux上安装Ruby
在linux下Ruby的安装要稍微复杂一些,推荐使用源码编译的方式安装,这样可以保
证安装的是最新版本。
首先到ruby主站 http://www.rubylang.
org/en/ 下载源代码,下载完毕后解压到目录,然
后使用以下命令:
./configure
./make; make install
执行上面的命令需要root权限,默认安装到/usr/local下。你也可以使用“./configure prefix=
自定义路径”来指定安装目录。
16
windows上的ruby oneclick
installer默认安装了RubyGems,但在Linux下我们需要手
动安装RubyGems。RubyGems是一个Ruby的包管理器,我们后边会讲到它。
首先从Rubyforge下载RubyGems的最近版本,地址如下:
http://rubyforge.org/projects/rubygems/
解压RubyGems以后到相应目录下输入ruby setup.rb,屏幕上打印一些日志以后会告诉
你安装成功,执行gem v
可以查看gem安装版本号。
§2.2 运行Ruby
下面,我们将以Windows平台下的Ruby环境举例如何运行Ruby。
§2.2.1 使用Ruby
将“Hello World”作为学习计算机语言第一个学写的程序,现在已经成为一种传统。该
程序最早出现在由Brian Kernighan和Dennis Ritchie写的经典计算机程序设计教程《The C
Programming Language》。我们来看看Ruby世界的“Hello World”:
在Windows中,打开命令行提示符窗口,在提示符上输入“Ruby”并回车,Ruby解释
器就会运行并等候输入程序。Ruby可执行文件应该包含在系统搜索路径内。
输入下面的程序:
print "Hello World!"
然后按Ctrl+D再按回车键,你就会看到Ruby执行程序的输出结果:
17
你也可以先将代码保存为文件,然后使用再Ruby解释器执行:
18
§2.2.2 使用FreeRIDE和SciTE
FreeRIDE是一个支持Ruby语言的免费IDE环境。FreeRIDE本身就是使用Ruby语言开
发,它也是Rubyforge上的重要项目之一。
可以使用FreeRIDE来编写调试和执行Ruby代码,FreeRIDE内置了交互式变成环境和
Ruby语言在线帮助,功能十分强大。
19
Scintilla是一个免费的源代码编辑控件,它完全开放源代码,并允许用户自由地用于
开源软件或是商业软件中。SciTE是用这个控件开发了一个编辑软件,在“OneClick
Ruby
Installer”中,SciTE集成了Ruby语言支持,使用起来非常方便。相比FreeRIDE,它的特点
就是使用简单。
20
§2.2.3 使用fxri
Fxri是一个Ruby交互帮助和控制台工具。它不仅可作为语言的在线帮助,而且可以用作
交互式Ruby解释器来执行程序。对于学习Ruby语言,fxri是一个非常方便的帮手。
不知你有没有听说过Fox ToolKit,它是相当轻巧的开放源代码的图形库。FXRuby 是
RubyForge 上的一个项目,提供了Ruby 语言使用Fox ToolKit 的接口。而Fxri 正是基于
FXRuby开发,Fxri同样是RubyForge上的项目。这样你应该可以猜到Fxri名字的由来继
21
Fxri同时集成了Rubyirb
和Rubyri
的功能,有了它,你可以抛开Rubyirb,
Rubyri
了,
但如果你用的不是Windows系统的话,算我没说何
§2.3 Rubyirb
Rubyirb
是交互式Ruby(Interactive Ruby)的简称,用来从标准输入读入并执行Ruby
代码的工具,像一个shell。
使用命令“irb”进入交互式模式,然后可以象输入命令行命令一样输入Ruby代码,代
码执行的结果会立刻显示:
22
§2.4 Rubyri
和Perl一样,Ruby也设计了嵌入式文档。 rubyri
就是查看文档的工具。Rubyri
的执行
命令为“ri”,例如你可以通过“ri String.new”来查询String类的new方法:
23
§2.5 RubyGems
RubyGems是Ruby社区流行的包管理工具,在以前如果要下载一个Ruby扩展或者应
用程序的话,你需要先下载相应的zip包,然后解压缩,再将应用或者扩展安装到Ruby对
应的目录中。但是有了RubyGems所有这些麻烦都没有了,你只需要一条命令就可以从远
程服务器上下载相应的包,如果相应的应用包含其他扩展,RubyGems会提示你从远程安
装所依赖的扩展。安装后 RubyGems会运行相应的程序生成rdoc帮助文档。当然你也可以
将软件包下载到本地运行RubyGems本地安装命令。
统一化的管理带来的好处就是简单,有了RubyGems包管理器,Ruby应用的安装将变
得前所未见的容易。RubyGems是Rubyforge下载量最大的项目之一,现在Ruby社区的应用
都在朝着RubyGems的方向发展,RubyGems也将成为Ruby事实上的包管理器标准。
24
RubyGems包管理器的可执行命令是“gem”,gem命令包含很多子命令和相应的选项,
例如:
gem h/
help
– 显示命令帮助
gem v/
version
– 显示Gems的版本号
25
第3章类与对象
Ruby是一种真正的面向对象程序设计语言,面向对象指以对象为中心的理论体系。
 封装(Encapsulation)
将内部结构和算法隐藏起来,以确保只有特定的过程(也叫方法)才能直接操作数据,
其结果是不能从外部直接使用数据构造,同时一旦内部构造发生变化也不会对外界造成不
良影响。这种隔离方法就叫做封装。
 继承
 多态(Polymorphism)
根据对象的不同选择合适的操作。在Ruby中的实现方法是,根据被调的对象的不同来
选择不同的方法。
虽然有很多语言都宣称自己是面向对象的,但是他们往往对面向对象的解释都一样,
大多是以自己特有的方式来解释什么是面向对象,而在实际情况中,这些面向对象语言又
采用了很多非面向对象的做法。
以 Java 为例:如果你想取一个数字取绝对值,java 的做法是:
int num = Math.abs(99);
也就是将一个数值传递给 Math 类的一个静态函数 abs 处理。为什么这么做?因为在
java 中,数值是基本类型不是类。
而在 Ruby 中,任何事物都是对象,也就是说,数字–99就是对象,取绝对值这样的
26
操作应该属于数字本身,所以Ruby的做法就是:
c = 99.
abs
在Ruby中,你所操作的一切都是对象,操作的结果也是对象。
§3.1 类的定义
类是对具有同样属性和同样行为的对象的抽象,Ruby中类的声明使用class关键字。定
义类的语法如下,
class ClassName
def method_name(variables)
#some code
end
end
类的定义要在class…end之间,在上面的格式中,ClassName是类名,类名必须以
大写字母开始,也就是说类名要是个常量。
看下面的例子:
class Person
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
若某个类已经被定义过,此时又用相同的类名进行类定义的话,就意味着对原有的类
的定义进行追加。
class Test
def meth1
puts "This is meth1"
end
end
27
class Test
def meth2
puts "This is meth2"
end
end
在Test类中,原有meth1方法,我们又追加了meth2方法,这时候,对于Test类
的对象,meth1和meth2同样可用。
§3.2 对象,属性和方法
类在实例化后生成对象,在强调对象归属于某类时,有时候我们也使用实例对象一词。
方法(Method)是对对象进行的操作。操作对象(被调)以self来表示。在Ruby中,除去
内部类的对象以外,通常对象的构造都是动态确定的。某对象的性质由其内部定义的方法
所决定。
看下面的例子,我们使用new方法构造一个新的对象,
class Person
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
people = Person.new('Tom', 'male', 15)
我们可以使用Person.new方法来创建一个Person类的实例对象。以@打头的变量
是实例变量,他们从属于某一实例对象,Ruby中实例变量的命名规则是变量名以@开始,
您只能在方法内部使用它。
initialize方法使对象变为“就绪”状态,initialize方法是一个特殊的方法,
28
这个方法在构造实例对象时会被自动调用。
对实例进行初始化操作时,需要重定义initialize方法。类方法new的默认的行为
就是对新生成的实例执行initialize方法,传给new方法的参数会被原封不动地传给
initialize方法。另外,若带块调用时,该块会被传给initialize方法。因此,不必
对new方法进行重定义。
在Ruby中,只有方法可以操作实例变量,因此可以说Ruby中的封装是强制性的。在
对象外部不可以直接访问,只能通过接口方法访问。
class Person
def name
@name
end
def gender
@gender
end
def age
@age
end
end
people = Person.new('Tom', 'male', 15)
puts people.name
puts people.gender
puts people.age
输出结果为:
Tom
male
15
在Ruby 中,一个对象的内部属性都是私有的。上面的代码中,我们定义了方法
29
name , gender , age 三个方法用来访问Person 类实例对象的实例变量。注意
name,gender,age访问只能读取相应实例变量,而不能改变它们的值。
我们也可以用成员变量只读控制符attr_reader来达到同样的效果。
class Person
attr_reader :name, :gender, :age
end
类似地,我们可以定义方法去改变成员变量的值。
class Person
def name=(name)
@name=name
end
def gender=(gender)
@gender=gender
end
def age=(age)
@age=age
end
end
people = Person.new('Tom', 'male', 15)
people.name = "Henry"
people.gender = "male"
people.age = 25
也可以用成员变量写控制符attr_writer来达到同样的效果。
class Person
attr_writer :name, :gender, :age
end
我们也可以使用attr_accessor来说明成员变量既可以读,也可以写。
class Person
attr_accessor :name, :gender, :age
end
30
也可以使用attr控制符来控制变量是否可读写。attr 只能带一个符号参数, 第二
个参数是一个 bool 参数,用于指示是否为符号参数产生写方法。它的默认值是
false,只产生读方法,不产生写方法。
class Person
attr :name, true #读写
attr :gender, true #读写
attr :age, true #读写
attr :id, false #只读
end
注意attr_reader,attr_writer,attr_accessor和attr不是语言的关键字,
而是Module模块的方法。
class Test
attr_accessor :value
end
puts Test.instance_methods Test.
superclass.public_methods
执行结果为:
value
value=
上面代码中,我们使用Test.instance_methods得到Test类所有的实例方法,
使用Test.superclass.public_methods得到Test父类所有的实例方法,然后相减
就得到Test类不包含父类的所有的实例方法。
由于instance_methods方法返回值为一个Array,所以我们作差值运算,Array
的具体操作后面章节会讲到。
31
也可以重定义方法,重定义一个方法时,新的定义会覆盖原有的定义。
下面的例子重定义类中的方法meth1,
class Test
def meth1
puts "This is meth1"
end
end
a = Test.new
a.meth1
class Test
def meth1
puts "This is new meth1"
end
end
a. meth1
执行结果为:
This is meth1
This is new meth1
重定义同一个类时,意味着对原有定义进行补充,不会覆盖原来的定义。而重定义方
法时,则会覆盖原有定义。
我们可以使用self标识本身,self和Java中的this有些类似,代表当前对象。
class Person
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
def <=>(other)
self.age <=> other.age
end
32
end
<=> 方法通常意思为比较,返回值为1,0
或1分别表示小于,等于和大于。
§3.3 继承
Ruby继承的语法很简单,使用 < 即可。
class Student < Person
def initialize(name, gender, age, school)
@name = name
@gender = gender
@age = age
@school = school
end
end
Ruby语言只支持单继承,每一个类都只能有一个直接父类。这样避免了多继承的复杂
度。但同时,Ruby提供了mixin的机制可以用来实现多继承。
可以使用super关键字调用对象父类的方法,当super省略参数时,将使用当前方
法的参数来进行调用。
class Base
def meth(info)
puts "This is Base #{info}"
end
end
class Derived < Base
def meth(info)
puts "This is derived #{info}"
super
end
end
obj1 = Derived.new
obj1.meth("test")
33
执行结果为:
This is derived test
This is Base test
如果传入的参数被修改再调用super的话,那么将会使用使用修改后的值。
class Base
def meth(info)
puts "This is Base #{info}"
end
end
class Derived < Base
def meth(info)
puts "This is derived #{info}"
info = "over"
super
end
end
obj1 = Derived.new
obj1.meth("test")
执行结果为:
This is derived test
This is Base over
§3.4 特殊方法与特殊类
特殊方法是指某实例所特有的方法。一个对象有哪些行为由对向所属的类决定,但是
有时候,一些特殊的对象有何其他对象不一样的行为,在多数程序设计语言中,例如C++
和Java,我们必须定义一个新类,但在Ruby中,我们可以定义只从属于某个特定对象的
方法,这种方法我们成为特殊方法(Singleton Method)。
34
class SingletonTest
def info
puts "This is This is SingletonTest method"
end
end
obj1 = SingletonTest.new
obj2 = SingletonTest.new
def obj2.info
puts "This is obj2"
end
obj1.info
obj2.info
执行结果为:
This is This is SingletonTest method
This is obj2
有时候,我们需要给一个对象定义一系列的特殊方法,如果按照前面的方法,那么只
能一个一个定义:
def obj2.singleton_method1
end
def obj2.singleton_method2
end
def obj2.singleton_method3
end
……
def obj2.singleton_methodn
end
这样做非常繁复麻烦,而且无法给出一个统一的概念模型,因此Ruby提供了另外一
种方法,
class << obj
35
……
end
obj是一个具体的对象实例,class << 代表它的特殊类。
class SingletonTest
def meth1
puts "This is meth1"
end
def meth2
puts "This is meth2"
end
end
obj1 = SingletonTest.new
obj2 = SingletonTest.new
class << obj2
def meth1
puts "This is obj2's meth1"
end
def meth2
puts "This is obj2's meth2"
end
end
obj1.meth1
obj1.meth2
obj2.meth1
obj2.meth2
执行结果为:
This is meth1
This is meth2
This is obj2's meth1
This is obj2's meth2
36
§3.5 类变量与类方法
类变量被一个类的所有实例对象共享,也可以被类方法访问到。类变量名以 ‘@@’
开始,例如 ‘@@number’。和全局变量,实例变量不同,类变量在使用前必须初始化:
class Person
@@number = 0 #使用前必须有初值
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
@@number += 1
end
end
类变量是私有的,在类外无法直接访问,你只能通过实例方法和类方法去访问它。
同样,类方法是属于一个类的方法,定义类方法时需要在方法前加上类名:
class Person
@@number = 0
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
@@number += 1
end
def Person.getNumber #类方法
return @@number
end
end
除了Person.getNumber这种方式定义类方法外,还可以使用其它方式定义类方法,
37
在后续章节可以陆续见到。
§3.4 存取控制
当你设计一个类时,你需要决定哪些属性和方法可以在类外被访问到,哪些属性和方
法在类外被隐藏。如果一个类有过多的属性和方法在类外可以被访问到,那么势必破坏这
个类的封装性。幸运的是在Ruby中,只能通过方法去改变一个类的属性,这样我们只需
要考虑方法的存取控制。
方法的存取控制有三种:
 公有方法(Public Method)
 方法在任何地方都可以被调用,这是方法的默认存取控制。除了initialize和
initialize_cpoy方法,他们永远是私有方法。
 保护方法(Protected Method)
 方法只能被定义这个方法的类自己的对象和这个类的子类的对象所访问。
 私有方法(private Method)
 方法只能被定义这个方法的类的对象自己访问,即使是这个类的其他对象也不能
访问。
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的概念有所区别,保护
方法的意思是方法只能方法只能被定义这个方法的类自己的对象和子类的对象访问,私有
方法只能被对象自己访问。
class Test
38
def method1 #默认为公有方法

end
protected #保护方法
def method2

end
private #私有方法
def method3
end
public
def test_protected(arg) #arg是Test类的对象
arg.method2 #正确,可以访问同类其他对象的保护方法
end
def test_private(arg) #arg是Test类的对象
arg.method3 #错误,不能访问同类其他对象的私有方法
end
end
obj1 = Test.new
obj2 = Test.new
obj1.test_protected(obj2)
obj1.test_private(obj2)
可以看到,和C++/Java相比,Ruby提供了更好的封装性。
也可以使用以下更简单的形式:
class Test
def method1
...
39
end
def method2
...
end
def method3
...
end
def methdo4
...
end
public :method1
protected :method2
private :method3, :method4
end
Ruby和C++/Java的一个显著不同是存取控制是程序运行时决定的而不是静态绑定的。
所以只有在访问一个受限制的方法时才会产生运行时错误。
§3.6 元类
在Ruby中一切都是对象。类和实例对象都是对象。这句话听起来有点拗口,让我们来看一
个例子:
class Person
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
a = Person.new('Tom', 'male', 15)
puts a.object_id => 22429840
puts Person.object_id => 22429960
40
没错,类也是对象,这是Ruby和C++/Java的一个显著不同,在C++/Java中,类
仅仅是一个数据抽象,并没有类也是对象这样的概念。而在Ruby中存在着元类的概念,
类也是对象,所有类都是元类的实例对象。和C++/Java相比,Ruby的面向对象程度更高。
可以看到,类对象和实例对象一样有自己的ojbect_id,你可以象调用一个实例对
象的方法一样去用它去调用类方法。所有类对象的类是Class类,Oject类是所有类的基
类。
irb(main):003:0> Object.class
=> Class
irb(main):004:0> Object.superclass
=> nil
这样,我们可以从另一个角度去理解类变量与类方法,类变量就是一个类对象的实例
变量,类方法就是指一个类对象类的特殊方法。
类方法具体可分为两种:第一种是在所有的类的父类Class中定义的,且被所有的类
所共享的方法;第二种是各个类所特有的特殊方法。
类方法中的self指的是类本身,这点需要牢记,这样我们可以使用多种方式定义类方
法。
class Test
#定义类方法方式1
def Test.meth1
# ...
end
#定义类方法方式2
def self.meth2
# ...
end
41
#定义类方法方式3
class << Test
def meth3
# ...
end
end
#定义类方法方式4
class << self
def meth4
# ...
end
end
end
§3.7 Ruby的动态性
可以重新定义同一个方法,
class RedefTest
def meth
puts "This is meth"
end
end
obj1 = RedefTest.new
obj1.meth
class RedefTest
def meth
puts "This is new meth"
end
end
obj1.meth
42
执行结果为:
This is meth
This is new meth
可以使用undef_method取消一个方法的定义,
class UndefTest
def meth
puts "This is meth"
end
end
obj1 = UndefTest.new
obj1.meth
class UndefTest
undef_method(:meth)
end
obj1.meth
执行结果为:
This is meth
test.rb:14: undefined method `meth' for #<UndefTest:0x2ac8240>
(NoMethodError)
§3.8 变量
变量名长度只受内存大小的限制。可以通过区分Ruby变量名的首字符来区分它是局
部变量、实例变量、类变量、全局变量还是常量。通常情况下,变量名的第二位字符以后是数
字、字母或下划线,但有的内部变量名比较特殊,如“$?”。
43
§3.8.1 局部变量
局部变量以小写字母或下划线开始。
num = 1
foo
局部变量的作用域起始于声明处,结束于该声明所在的块、方法定义、类/模块定
义的结尾。
2.times {
p defined?(num)
num = 10
p num
}
输出为:
nil
10
nil
10
即使声明部分未被解释器执行仍有效,因为已经经过解释器的处理。
v = 1 if false
p defined?(v)
p v
输出为:
"localvariable"
nil
但若块已经变成过程对象的话,则局部变量将一直持续到该过程对象终结为止。
44
若多个过程对象引用同一个作用域的话,局部变量将被这些对象所共享。
(todo
例子)
§3.8.2 实例变量
以@开始的变量是实例变量,实例变量属于特定的对象。
class Person
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
上面的例子中,@name, @gender,@age都是实例变量。可以在类或子类的方法中
引用实例变量。若引用尚未被初始化的实例变量的话,其值为nil。
§3.8.3 类变量
以@@开始的变量是类变量。类变量在类的定义中定义,可以在类的特殊方
法、实例方法等处对类变量进行赋值和引用。类变量被类,类的子类和他们的实
例对象共享。
class Person
@@number = 0 #使用前必须有初值
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
45
@@number += 1
end
end
类变量是私有的,在类外无法直接访问,你只能通过实例方法和类方法去
访问它。可以把类变量看作一种被类、子类以及它们的实例所共享的全局变量。
模块中定义的类变量(模块变量)被所有包含该模块的类所共享。
module TestModule
@@foo = 10
end
class Klass
include Foo
p @@foo += 1 # => 11
end
class Base
include Foo
p @@foo += 2 # => 12
end
§3.8.4 全局变量
以$开始的变量是全局变量,全局变量可以在程序的任何地方加以引用。全局变量无需
变量声明。引用尚未初始化的全局变量时,其值为nil。
Ruby运行时环境预定义了一系列的全局变量,有关预定义的全局变量的信息,请参
见附表。
46
§3.8.5 常量
常量以大写字母开始,常数的定义和初始化由赋值过程完成。
PI = 3.14
E = 2.71
若对已定义的常数进行赋值的话,会出现警告信息。若引用未定义的常数会引发
NameError异常。
PI = 3.14
obj1 = 2 * PI * 10
PI = 3.1415 # warning: already initialized constant PI
obj2 = Foo #uninitialized constant Foo (NameError)
常量可以定义在类和模块中,不能定义在方法中。
class Meth
PI = 3.14 #OK
end
def circle_area(arg)
PI = 3.14 #ERROR
PI * arg * arg
end
若想在外部访问类或模块中的常数时,要使用“::”操作符。
class Meth
PI = 3.14
end
def circle_area(arg)
Math::PI * arg * arg
end
在类定义表达式生成类对象的同时,还会将类对象赋值给一个与该类同名的常数,引
47
用类名也就是引用该常数。
class Test
end
p Test.class #Class
p Test #test
若想访问Object类中的常数(顶层的常数)时,也需要也使用"::"操作符,但操作符左边
为空。
§3.8 与定义有关的操作
§3.8.1 alias
Alias关键字给方法或全局变量添加别名。可以给方法名指定一个标识符或Symbol作
为别名。给方法添加别名时,别名方法将和此刻的原始方法绑定,此后即使重新定义了原
始方法,别名方法仍然保持着重定义前的老方法的特性。若改变了某方法的内容后,又想
使用修改前的方法时,别名会很有用。也可以使用Module#alias_method给方法添加别名。
# 定义meth方法
def meth
puts "This is meth"
end
#设定别名
alias :orig_meth :meth
#重定义foo
def meth
48
puts "This is new meth"
end
p meth
执行结果为:
This is new meth
nil
给全局变量设定别名意味两个名称指向同一个全局变量。当你向一个赋值时,另一个
也会被改变。
$abc = 1
alias $xyz $abc
$xyz = 2
p [$abc, $xyz] # => [2, 2]
但是不能给正则表达式中的变量$1,$2等添加别名,另外,有些全局变量对于解释
器来说是举足轻重的,若重新定义它们的话,有时会影响解释器的正常工作。
§3.8.2 undef
undef用来取消一个方法的定义,也可以使用Module#undef_method方法取消方法的
定义。undef会取消方法名和方法定义之间的关系,即使超类中有同名方法,调用时也会
引发异常。
class Base
def meth
puts "This is Base#meth"
end
end
49
class Derived < Base
def meth
puts "This is Derived#meth"
end
end
class Test1 < Derived
def meth
puts "This is Test1#meth"
end
undef_method(:meth)
end
obj1 = Test1.new
obj1.meth
执行结果为:
Tes1.rb:22: undefined method `meth' for #<Test1:0x2ac7c88>
(NoMethodError)
而Module#remove_method方法只负责取消当前类中方法名和方法定义之间的关系,
父类的同名方法仍可调用,这点差别非常重要。
class Base
def meth
puts "This is Base#meth"
end
end
class Derived < Base
def meth
puts "This is Derived#meth"
end
end
class Test2 < Derived
def meth
puts "This is Test2#meth"
end
remove_method(:meth)
50
end
obj2 = Test2.new
obj2.meth
执行结果为:
This is Derived#meth
用alias添加别名或用undef取消定义时,会修改类的接口,而不受父类的限制。继承和
Mix-in的功能都是在类中添加方法,而undef则可以取消方法。但是,如果取消了类所必
需的方法(被其他方法所调用的方法)的话,其后果不堪设想。
§3.8.3 defined?
Defined?用来判断表达式是否定义。若表达式尚未定义,则返回nil,若已经定义,则
返回一个字符串描述该表达式的种类。
defined? Val #=> nil
defined? true #=> “true”
defined? $* #=> "globalvariable"
defined? Array #=> "constant"
defined? Math::PI #=> "constant"
defined? num = 0 #=> "assignment"
defined? 100 #=> "expression"
defined? 100.times #=> "method"
虽然defined?看起来像一个方法,实际上是Ruby语法中的操作符,因此不会对参
数进行计算。因此下面的表达式并不会输出“abc”。
defined? print("abc\n")
如果是方法未定义,或方法使用undef或Module#remove_method取消了原有定
51
义,defined?都将返回nil。
注意如果一个方法以大写字母开头,使用defined? 判断时需要在方法名后添加"()"时,
否则方法名会被当做常数处理。
def Foo(arg)
end
p defined? Foo # => nil
p defined? Foo() # => "method"
Foo = 1
p defined? Foo # => "constant"
还可以使用下列特殊用法:
 判断yield是否可用
defined? yield
若yield调用可用,则返回真,具体返回值为字符串"yield"。它的作用同block_given?一
样,可以判断能否以带块方式来调用某方法。
class Base
def foo
puts defined? yield
end
end
a = Base.new
a.foo
a.foo {}
执行结果为:
nil
yield
 判断super是否可用
defined? super
52
若super可被调用,则返回真, 具体返回值为字符串"super"。
class Base
def foo
end
end
class Derived < Base
def foo
puts defined? super
end
def fun
puts defined? super
end
end
obj = Derived.new
obj.foo
obj.fun
执行结果为:
super
nil
 返回没有赋值但已经定义的局部变量.
defined? a = 1 #=> assignment
p a # => nil
 在正则表达式中使用
/(.)/ =~ "foo"
p defined? $& # => "$&"
p defined? $1 # => "$1"
p defined? $2 # => nil
53
第四章 基本类型
§4.1 Array
Array也称作数组,是一系列元素的有序集合。你可以显式使用Array类的new方法来创建
一个数组对象,你也可以用方括号包围起来一些以逗号分隔的数字或字符串构成一个数组。
irb(main):007:0> a = [ "first" "second" "third" ]
=> ["firstsecondthird"]
irb(main):008:0> a = [ "first", "second", "third" ]
=> ["first", "second", "third"]
irb(main):009:0> a.class
=> Array
irb(main):010:0> a.length
=> 3
irb(main):011:0> a[0]
=> "first"
irb(main):012:0> a[1]
=> "second"
irb(main):013:0> a[2]
=> "third"
irb(main):014:0> a[3]
=> nil
irb(main):015:0> b = Array.new
=> []
irb(main):016:0> b.class
=> Array
irb(main):017:0> b.length
=> 0
irb(main):018:0> b[0] = "first"
54
=> "first"
irb(main):019:0> b[1] = "second"
=> "second"
irb(main):020:0> b
=> ["first", "second"]
数组可以使用 [] 来索引,其实 [] 是Array 类的一个方法,它甚至可以被子类覆盖
(overridden)。Ruby中比较有趣的是有多种对数组的索引方法,你可以用负数来索引数组。
负数表示从尾部开始,例如索引为-1表示最后一个元素,索引为-2表示倒数第二个元素,
以此类推。
irb(main):021:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):022:0> a[1]
=> 5
irb(main):023:0> a[2]
=> 4
irb(main):024:0> a[9]
=> nil
你也可以使用一对数来索引数组,第一个数表示开始位置,第二数表示从开始位置起的元
素数目。
irb(main):025:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):026:0> a[1, 3]
=> [2, 3, 4]
irb(main):027:0> a[3, 1]
=> [4]
irb(main):028:0> a[3,
1]
=> [3]
55
你甚至可以用一个范围来索引数组,.. 表示包含尾部元素,... 表示不包含尾部元素。
irb(main):029:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):030:0> a[1..3]
=> [2, 3, 4]
irb(main):031:0> a[1...3]
=> [2, 3]
§4.2 Hash
Hash也称作哈希表哈希表,类似于数组但是每个元素都有索引,有时候也被称作关
联数组,哈希数组或字典。哈希表和数组不同,数组只能使用数字索引,而哈希表则可以
使用任何对象索引。哈希表和数组的另一个显著不同是哈希表中的元素是无序的。在Ruby
中每个哈希表都是Hash类的对象。
在哈希表中,我们称索引为Key,被索引的元素称为Value。
我们可以使用=>连接的元素来创建一个哈希表,注意哈希表外部是使用大括号包围。
irb(main):032:0> h = { "first" => "Amy", "second" => "Mike",
"third" => "Tom" }
=> {"third"=>"Tom", "second"=>"Mike", "first"=>"Amy"}
irb(main):033:0> h.length
=> 3
irb(main):034:0> h["first"]
=> "Amy"
irb(main):035:0> h['second']
=> "Mike"
irb(main):036:0> h[100] = "Henry"
=> "Henry"
irb(main):037:0> h["nine"] = "Rose"
=> "Rose"
irb(main):038:0> h
56
=> {"third"=>"Tom", "second"=>"Mike", 100=>"Henry",
"first"=>"Amy", "nine"=>"Rose"}
§4.3 Number
Ruby支持整数类型和浮点数类型。整数可以是任意长度(这个长度只和内存大小有关)。
在一定范围内的整数被视为Fixnum类的对象。超出这个范围的整数被视为Bignum类的对
象。
num = 81
6.times do
puts "#{num.class}: #{num}"
num *= num
end
运行结果:
Fixnum: 81
Fixnum: 6561
Fixnum: 43046721
Bignum: 1853020188851841
Bignum: 3433683820292512484657849089281
Bignum:
11790184577738583171520872861412518665678211592275841109096961
和C/C++相同,Ruby规定以0开头的数为八进制数,以0x开头的数为十六进制数,
以0b开头的数为二进制数。
irb(main):001:0> 16
=> 16
irb(main):002:0> 020
=> 16
irb(main):003:0> 0x10
=> 16
irb(main):004:0> 0b10000
57
=> 16
一个数中间可以用下划线连接,下划线自动被忽略。
irb(main):005:0> 123_456_789
=> 123456789
可以使用"?\Cx"
或"?\cx"生成控制字符。
如果一个数包含小数点或者包含"e",那么这个数将被转为Float类的对象。
irb(main):012:0> 1.0.class
=> Float
irb(main):013:0> 1.0e3.class
=> Float
§4.4 String
String也称作字符串,是单引号或双引号包围起来的一串字符。单引号和双引号的意义
有所不同,双引号包围的字符作变量替换,单引号包围的变量不做替换。可以在字符串中
使用 #{expr} 嵌入代码。
irb(main):022:0> "The seconds in a day is: #{24*60*60}"
=> "The seconds in a day is: 86400"
irb(main):023:0> 'The seconds in a day is: #{24*60*60}'
=> "The seconds in a day is: \#{24*60*60}"
也可以使用 %q 和 %Q 来生成字符串对象。%q 相当于单引号,%Q相当于双引号。
irb(main):051:0> %q/Single quote/
=> "Single quote"
irb(main):052:0> %Q/Double quote/
=> "Double quote"
irb(main):053:0> %q/ #{50*50} /
=> " \#{50*50} "
58
irb(main):054:0> %Q/ #{50*50} /
=> " 2500 "
%q 和 %Q 后面的第一个字符为分隔符。二哥分隔符之间的字符被认为一个是字符串。
但是如果这个分隔符是 [ { <, 那么结束标志为匹配的 ] } >。
irb(main):055:0> %q{This is a string}
=> "This is a string"
irb(main):056:0> %Q[This is a string]
=> "This is a string"
irb(main):057:0> %q<This is a string>
=> "This is a string"
你也可以使用“Here Document”的方法来生成字符串,这种方法规定 << 之后的字符
串作为结束标志。
string = <<END_OF_STRING
With publication started in June 1948 and a current circulation of 3 million,
People's Daily is the most influential and authoritative newspaper in China.
According to UNESCO, it takes its place among the world top 10.
END_OF_STRING
需要注意,表示结尾的END_OF_STRING必须放在行首。
§4.5 Range
Range也称作范围,用来表示一个都是连续的值的序列。可以使用 .. 和 ... 操作符来产
生Range,前者表示包含最后一个元素,后者表示不包含最后一个元素。Range对象所属的
类是Range。注意Range和Array是不同的,可以使用Range类的to_a方法将一个Range对
象转化为Array对象。
irb(main):003:0> (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):004:0> ('bar'..'bat').to_a
59
=> ["bar", "bas", "bat"]
irb(main):005:0> (1...10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
Range除了表示一个序列外还可以出现在条件语句中。在条件语句中,可以把Range
看作一个双向开关,当第一个条件满足时打开开关,当第二个条件满足时关闭开关。
a = [0, 1, 2, 3, 4, 5, 6]
a.each do |i|
print i, " " if i == 1 .. i == 5
end
执行结果为
1 2 3 4 5
上述代码中if i == 1 .. i == 5表示只有满足i==1且不满足i==5时条件为真,
当不满足i==1或满足i==5时条件为假,直观地看,就是表示元素需要位于范围之中。
可以使用 === 来测试一个元素是否在某个范围:
irb(main):093:0> (1..10) === 3
=> true
irb(main):094:0> (1..10) === 30
=> false
irb(main):095:0> (1..10) === 2.71828
=> true
irb(main):096:0> ('a'..'f') == 'c'
=> false
irb(main):097:0> ('a'..'f') == 'g'
=> false
Range也可以位于case语句之内:
score = 98
case score
when 85..100 then puts "A"
when 70...85 then puts "B"
when 60...70 then puts "C"
60
else puts "D"
end
执行结果为:
A
§4.6 Symbol
Symbol是个简单的对象,它使用名字作为唯一的标识符。Symbol对象代表解释器内部
一个唯一的名字。Symbol的产生很简单,只需要给一个字符序列前添加“:”或使用
“to_sym”方法。 Symbol对象从属于Symbol类。
String和Symbol两者具有紧密的联系。每个symbol都有个字符串的名字(可以使用
to_s方法得到)。而每个String可以请求它的相应symbol(通过to_sym方法)。String和
Symbol是紧密相联的,但它们不是同一个东西,他们分别是String类和Symbol类的对象。
有读者可能会问,为什么要存在Symbol对象呢?因为symbol可以大大提高速度。
Symbol的内部表示是一个整数,用来做Hash表中检索字符串的关键字,而Ruby语言
执行时解析器、运算器需要大量的类名字、方法名字的检索,这可以大大加快解析和执行时
字符串查找的速度。
想想,如果没有Symbol,如果需要用方法名称作为参数时,我们必须给一个字符串
用来表示方法的名称,解释器处理时首先要作字符串解析,然后才能找到出相应的方法,
而如果使用Symbol会大大加快这一速度。
在使用中,Symbol往往表示一个名字,例如一个变量 foo的值为1,那么 :foo可以
理解为变量名,如果直接引用foo,会得到1,但如果是 :foo就指变量名本身。
61
Symbol对象是唯一的。每次你在代码中使用:test, 你是要引用一个名字为"test"的
Symbol类的对象。Ruby保证系统中只有一个名字为test的Symbol对象, 所以所有对:test
的引用都将引用同一个对象。
irb(main):001:0> module One
irb(main):002:1> class Test
irb(main):003:2> end
irb(main):004:1> $f1 = :Test
irb(main):005:1> end
=> :Test
irb(main):006:0> module Two
irb(main):007:1> Test = 1
irb(main):008:1> $f2 = :Test
irb(main):009:1> end
=> :Test
irb(main):010:0> def Test()
irb(main):011:1> end
=> nil
irb(main):012:0> $f3 = :Test
=> :Test
irb(main):013:0> $1.object_id
=> 4
irb(main):014:0> $2.object_id
=> 4
irb(main):015:0> $3.object_id
=> 4
§4.7 正则表达式
正则表达式的类是Regexp,可以使用/或%r生成正则表达式。
irb(main):103:0> a = /\s*[af]/
=> /\s*[af]/
irb(main):104:0> a.class
=> Regexp
62
irb(main):105:0> b = %r{\s*[af]}
=> /\s*[af]/
irb(main):106:0> b.class
=> Regexp
irb(main):107:0> c = Regexp.new('\s*[af]')
=> /\s*[af]/
irb(main):108:0> c.class
=> Regexp
你可以使用Regexp#match(string)方法或者=~运算符来匹配正则表达式,你也可以使
用!~来测试是否不匹配。
irb(main):113:0> sentence = "This is a dog."
=> "This is a dog."
irb(main):114:0> sentence =~ /dog/
=> 10
irb(main):115:0> sentence =~ /a/
=> 8
irb(main):116:0> /o/ =~ sentence
=> 11
irb(main):117:0> sentence !~ /xyz/
=> true
另外,在匹配正则表达式时,会将匹配到的字符串存放在 $& 变量中,$' 变量中存放已
经匹配过的字符序列,$` 变量中存放还未匹配的字符序列。
irb(main):118:0> sentence = "This is a dog."
=> "This is a dog."
irb(main):119:0> sentence =~ /a/
=> 8
irb(main):120:0> puts $&
a
=> nil
irb(main):121:0> puts $'
dog.
=> nil
63
irb(main):122:0> puts $`
This is
=> nil
第五章 代码块和迭代器
§5.1 代码块(Block)
§5.1.1 什么是代码块
在Ruby中在在大括号之间的代码或放在do/end之间的代码是一个代码块。代码块只
能出现在一个方法的后边,它紧接在方法最后一个参数的同一行上。代码块的内容并不会
被马上执行,当执行到被调用的方法时,解释器的运行时环境会记住代码块出现的现场,
然后执行被调用的方法。
[1,2,3,4,5].each { |i|
puts i
}
[1,2,3,4,5].each do |i|
puts i
end
一般的使用习惯是:(to-do 具体解释)
64
 当关心边际(side effect)效应时使用 do/end。
 当关心返回结果应时使用大括号。
§5.1.2 代码块与对象
代码块并不是对象,但可以方便的转化为Proc类的对象。有三种转化的方法:
 将一个代码块传递给最后一个参数以&开始的方法。
def meth1(p1, p2, &block)
puts block.inspect
puts block.call
end
meth1(1, 2) { "This is a block" }
 使用Proc.new方法,后边的参数为一个代码块:
block = Proc.new { "a block" }
 调用Kernel.lambda方法:
block = lambda { "a block" }
前两种方法是等价的,而第三种方法,用 lambda 生成的 Proc 对象和用 Proc.new
生成的 Proc 对象之间是有差别的。这是一个微妙的差别,这个差别与 return 关键字相
关。lambda 中的 return 从 lambda 返回。而 Proc 中的 return 从外围方法返回。
可以看以下两个例子:
def test_proc
p = Proc.new { return 1 }
p.call
puts "Never come here"
end
65
test_proc #=> 1
执行后"Never come here"不会被输出,执行p.call相当于在test_proc方法
内执行了return语句。
def test_lambda
p = lambda { return 1 }
result = p.call
puts "The value is: #{result}"
end
test_lambda
执行后的结果为:
The value is: 1
可见使用lambda生成的Proc对象执行call方法调用时,return表示从lambda包
围得块内返回。
在一个代码块中执行next语句会导致代码块返回。返回值就是next语句后带的参数。
如果next后没有参数,那么返回值为nil。
def meth2
result = yield
"The block result is #{result}"
end
puts meth2 { next 9 }
pr = Proc.new { next 100 }
puts pr.call
66
pr = lambda { next }
puts pr.call
执行结果为:
The block result is 9
100
nil
§5.2 迭代器(Iterator)
§5.2.1 什么是迭代器
简单的讲,一个迭代器就是一个能接受代码块的方法。当初为了进行迭代操作而设置
了带块方法,所以现在它仍然常常被称作迭带器。
[1,2,3,4,5].each { |i|
puts i
}
上述代码中,each方法反复调用代码块,我们称each方法为一个迭代器。
迭代器(Iterator)即指调用带块方法。实际上,在早期版本的 Ruby 中,使用代码块的
方法被称为迭代器,因为它们就是被设计来实现循环迭代的。但是在Ruby发展过程中,
代码块的用途在后来已经得到了很大的增强,从最初的循环抽象到任何事情。可以将那些
进行迭代操作的方法叫做迭代器,但如果将所有带块方法的调用过程都看作迭带器的话,
并不合适而且概念上会引起混乱
 
67
§5.2.2 使用迭代器
#一个使用迭代器的简单例子,数组中每一个元素作为参数执行其后的代码块
['This', 'is', 'a', 'dog'].each do |entry|
print entry, ' '
end
执行结果为:
This is a dog
#另一个使用迭代器的例子,代码块可以访问其外的数据
factorial = 1
1.upto(10) do |i|
factorial*= i
end
puts factorial
执行结果为:
3628800
#代码块的返回值可以被调用者使用
b = [1, 2, 3, 4, 5].map do |entry|
entry * entry
end
print b.inspect
执行结果为:
[1, 4, 9, 16, 25]
#代码块也可以使用一个以上的参数
result = (0..100).inject(0) do |sum, i|
sum + i
68
end
print result
执行结果为:
5050
§5.2.3 yield
在方法中可以使用yield来执行代码块的内容,就好像传入的代码块是这个方法的一
部分一样。每当碰到一个yield调用,代码块的内容就会被执行一次。当代码块执行结束后,
程序会回到yield的那一行继续向下执行。
def twoTimes
yield
yield
end
twoTimes { puts "Hello World!" }
执行结果为:
Hello World!
Hello World!
你可以使用yield操作传参数给一个代码块,并且从代码块取回返回值。
def fibonacii(max)
f1, f2 = 1, 1
while f1 <= max
yield f1
f1, f2 = f2, f1+f2
end
end
69
fibonacii(1000) { |f| print f, " " }
执行结果为:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
在这个例子中,yield接收一个参数,这个参数将会在执行的时候传递给指定的代码
块。在代码块中,接收的参数使用两个竖线括起来放在代码块的头部。yield操作也可以有
返回值,yield操作的返回值就是代码块中最后一个表达式的值。
§5.2.4 编写自己的迭代器
def factorial(count, &block)
value = 1
1.upto(count) do |i|
value = value * i
block.call(i, value)
end
end
factorial(5) do |i, sum|
puts "factorial(#{i}) = #{sum}"
end
执行结果为:
factorial(1) = 1
factorial(2) = 2
factorial(3) = 6
factorial(4) = 24
factorial(5) = 120
也可以将传入的代码块保存以供以后使用:
class Mathematics
70
def initialize(&block)
@block = block
end
def factorial(max)
value = 1
1.upto(max) do |i|
value = value * i
@block.call(value)
end
end
end
the_value = Mathematics.new do |count|
puts "Current value is #{count}"
end
the_value.factorial(5)
执行结果为:
Current value is 1
Current value is 2
Current value is 6
Current value is 24
Current value is 120
第六章 表达式
Ruby语言的一切都有返回值,这是Ruby语言和其他程序设计语言的一个显著不同。
irb(main):006:0> a = b = c = 0
=> 0
irb(main):007:0> print "\n"
71
=> nil
同样,if和case语句也有返回值,if和case语句的返回值就是if和case中最后一个执行语
句的值。
irb(main):014:0> if( 1+1 == 2)
irb(main):015:1> "Like in school."
irb(main):016:1> else
irb(main):017:1* "What a surprise!"
irb(main):018:1> end
=> "Like in school."
§6.1 运算符
和其他程序设计语言一样,Ruby中含有丰富的运算符。但是在Ruby中,大多数运算
符实际上是方法调用。例如 a+b,其实真实执行的是 a.+(b),调用a对象的+方法,b作为
这个方法的参数。这样带来了相当的灵活性,你可以改变原有运算符的语义从而赋予它新
的含义。
以下代码仅仅作为一个例子重写Fixnum类的 + 方法,赋予两个定长整数相加新的含
义。
irb(main):001:0> class Fixnum
irb(main):002:1> alias the_plus +
irb(main):003:1* def +(integer)
irb(main):004:2> the_plus(integer) * 2
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> 1+1
=> 4
irb(main):032:0> 2+2
=> 8
irb(main):132:0> 2+5
72
=> 14
对于运算符(+ - * / % ** & | ^ << >> && ||),Ruby有相应形式的赋值运算符缩写
形式+=, -=等。
运算符优先级:
::
[]
+(一元) -(一元) ! ~
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === != =~ !~
&&
||
.. …
?:
= += -= *= /=(所有的赋值运算符缩
写)
not
and or
以下运算符不能作为方法调用,也就是说不能改变以下运算符的含义:

!
not
&&
And
||
Or
73
::
=
+= -= *= /=(所有的赋值运算符
缩写)
?:
§6.2 命令替换
在Shell中,可以使用反引号(`)执行命令替换。
`date` =〉Mon Nov 27 11:07:22 CST 2006
`pwd` =〉/usr/include
Ruby也有这个功能。在Ruby中,可以使用反引号或%x来执行命令替换。命令替换表
达式的返回值就是命令执行的输出结果。命令执行的返回值存储在全局变量$?中。
irb(main):2134:0> %x{echo "Hello World!"}
=> "\"Hello World!\"\n"
反引号的默认行为是执行命令替换,同样,我们也可以重写它,赋予它新的含义。
alias old_backquote `
def `(cmd)
result = old_backquote(cmd)
if $? != 0
fail "Command #{cmd} failed: #$?"
else
puts "Command #{cmd} success."
end
result
end
print `date`
print `data`
执行结果为:
74
Command date success.
Mon Jan 15 21:48:16 CST 2007
Command uname success.
Linux
§6.3 赋值运算符
todo
定义。
赋值运算的返回值就是左值的值,所以可以进行链式赋值。
irb(main):001:0> a = b = c = 5
=> 5
irb(main):002:0> a = ( b = 1 + 2 ) + 5
=> 8
Ruby的基本赋值有两种形式,一种左边是一个对象或变量,这时把右边的值或变量
的引用赋予左边。这种赋值运算由语言本身提供。
irb(main):003:0> str = "This is a dog."
=> "This is a dog."
irb(main):004:0> num = 100
=> 100
另一种形式的赋值运算左边是一个类的实例的某一属性,这时候是执行这个类的方法,
方法名称为“属性=”。方法的返回值就是右值的值,你可以重写这个方法从而赋予它新的
含义。
irb(main):001:0> class Test
irb(main):002:1> def num=(num)
irb(main):003:2> @num = num
irb(main):004:2> end
irb(main):005:1> end
=> nil
75
irb(main):006:0> t = Test.new
=> #<Test:0x2e20568>
irb(main):007:0> t.num = 10
=> 10
76
§6.4 并行赋值
Ruby中另一个有趣的地方是支持并行赋值。例如,交换两个变量a,b的值可以写为:
a,b = b,a
Ruby会先从左到右依次计算 = 右边的表达式,然后再执行赋值的动作。
irb(main):008:0> x = 0
=> 0
irb(main):009:0> a,b,c = x, x+=1, x+=2
=> [0, 1, 3]
如果左边的变量比右边的多,那么多余的变量会被赋为nil.
irb(main):001:0> x, y, z = 1, 2
=> [1, 2]
irb(main):002:0> print z
nil=> nil
如果右边的变量或值比左边的多,那么多余的会被忽略。
irb(main):001:0> x, y = 1, 2, 3 # 3将被忽略
=> [1, 2, 3]
也可以在数组赋值时使用并行赋值。
irb(main):001:0> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> x, y = a
=> [1, 2, 3, 4, 5]
irb(main):003:0> puts x, y
1
2
=> nil
在对数组进行并行赋值时可以使用*,*出现在左边最后一个变量时,表示将数组中
所有剩余的值赋给这个变量。
77
irb(main):001:0> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> x,*y = a
=> [1, 2, 3, 4, 5]
irb(main):003:0> puts x
1
=> nil
irb(main):004:0> puts y
2
3
4
5
=> nil
*出现在右边最后一个变量时,和左边类似。
a = [1, 2, 3, 4]
b, c = 9, a =〉b == 9, c == [1, 2, 3, 4]
b, c = 9, *a =〉b == 9, c == 1
b, *c = 9, a =〉b == 9, c == [[1, 2, 3, 4]]
b, *c = 9, *a =〉b == 9, c == [1, 2, 3, 4]
§6.5 嵌套赋值
在赋值中,左边的变量可以使用括号括起来。这样括号内的变量被视作位于一个层次。
b, (c, d), e = 1,2,3,4 =〉b == 1, c == 2, d == nil, e == 3
b, (c, d), e = [1,2,3,4] =〉b == 1, c == 2, d == nil, e == 3
b, (c, d), e = 1,[2,3],4 =〉b == 1, c == 2, d == 3, e == 4
b, (c, d), e = 1,[2,3,4],5 =〉b == 1, c == 2, d == 3, e == 5
b, (c,*d), e = 1,[2,3,4],5 =〉b == 1, c == 2, d == [3, 4], e == 5
78
§6.6 其他赋值
Ruby支持自加(+=)和自减运算符。和C/C++/Java一样,a = a + 2可以写成a+=2。其
它类似的运算符还有%= ~= /= = += |= &= >>= <<= *= &&= ||= **=。
我们经常可以遇到类似这样的语句words[key] ||= [],他与words[key] = words[key] || []
等价,意思是如果Hash表words[key]的值为空时,对words[key]赋值为一个新建的空数组,
否则不变。
相应的,对于
num = 1 if num.nil?
num = 1 unless num
Ruby中习惯写为 num ||= 1,这样代码更简洁。
§6.7 条件运算
布尔运算符
在Ruby中定义nil和false为假,其他值为真。注意,和C/C++不同的是0并不被解释
为假,空字符串也一样。
Ruby 支持常见的布尔运算符,例如 and 和 &&,而且还引入一个新的布尔运算符
‘defined?’。
和其他程序设计语言一样,and 和 && 代表与关系。
or 和 || 代表或关系。
Not 和 ! 代表非关系。
79
如果参数没有定义 defined? 返回nil,否则返回一个描述串用来描述参数信息。
irb(main):013:0> defined? 1
=> "expression"
irb(main):014:0> defined? dummy
=> nil
irb(main):015:0> defined? printf
=> "method"
irb(main):016:0> defined? String
=> "constant"
irb(main):017:0> defined? $_
=> "globalvariable"
irb(main):018:0> defined? Math::PI
=> "constant"
irb(main):019:0> defined? a = 0
=> "assignment"
irb(main):020:0> defined? 30.abs
=> "method"
条件运算符
Ruby支持一系列条件运算符,==, ===, <=>,=~,eql? 等等,equal?。除了<=> 其他
的都是类方法。 == 和 =~ 有否定形式 != 和 !~。
If 和unless
Ruby中的if和其他程序设计语言中的if大同小异:
if x == 5 then
print “The value of x is 5.”
elsif x == 0 then
print “The value of x is 0.”
else
print “The value of x is ”, x
end
也可以省略then:
if x == 5
80
print “The value of x is 5.”
elsif x == 0
print “The value of x is 0.”
else
print “The value of x is ”, x
end
如果为了代码紧凑而将代码放到同一行则不能省略then:
if x == 5 then print “The value of x is 5.”
elsif x == 0 then print “The value of x is 0.”
else print “The value of x is ”, x
end
也可以使用冒号分隔,这样代码更紧凑 :
if x == 5: print “The value of x is 5.”
elsif x == 0: print “The value of x is 0.”
else print “The value of x is ”, x
end
正如我们前面所说的,if是一个表达式,它有自己的返回值。你可以忽略这个返回值,但
是他确实存在。If语句的返回值就是最后执行的语句的值。
x = 10
str = if x == 5: "x==5"
elsif x == 0: "x==0"
else "x==?"
end
执行后str的内容为:x== ?
Ruby也支持if的否定形式unless,unless的语法和if没有差别。
unless x != 5
print “The value of x is 5.”
else
print “The value of x is not 5.”
end
81
Ruby也支持C/C++的 ?:运算符。
str = x == 5? "x==5":"x==?"
Ruby也从Perl那里继承了一个很好的语法,你可以将条件写到表达式的后边。
puts "a = #{a}" if debug
print total unless total.zero?
§6.8 case表达式
Ruby中的case语句非常强大,首先我们来看一个基本用法:
grade = case
when point >= 85: 'A'
when point >= 70 && point < 80: 'B'
when point >= 60 && point < 70: 'C'
when point < 60: 'D'
else 'E'
end
这里case语句的作用和if表达式类似,case语句的返回值就是最后一个执行的表
达式的值。和if语句类似,如果写在同一行的话需要加then或冒号。
另一种也是最常用的形式是在case后列出目标,然后每个语句依次和目标比较:
case input_line
when "debug"
print "We are in debug mode."
when /p\s+(\w+)/
dump_variable($1)
when "quit", "exit"
exit
else
print "Illegal command: #{input_line}"
end
另一个例子:
82
Season = case month
when 3..5 : "Spring"
when 6..8 : "Summer"
when 9..11: "Autumn"
when 12..2: "Winter"
else "Error"
end
Ruby提供了一个运算符===,只要一个类提供了===方法,那这个类的对象就可以出现在
case语句中。例如对于正则表达式定义了===为模式匹配。
Ruby中,所有类的基类是Class类,所有类实例都是Class类的实例(todo)
。它定
义===的含义为为参数所提供是否为实例的类或父类。
case shape
when Square, Rectangle
# ...
when Circle
# ...
when Triangle
# ...
else
# ...
end
§6.9 循环
§6.9.1 Loop
Loop循环始终执行其后的方法块,直到break退出。
x = 0
loop do
x += 1
83
if x <= 5: print x, " "
else break
end
end
执行结果为:1 2 3 4 5 。
§6.9.2 While
当条件为真时While循环继续,条件为假时退出循环。
x = 0
while x < 10
x += 1
end
§6.9.3 Until
Until和While厢房当条件为假时While循环继续,条件为真时退出循环。
x = 0
until x == 9
x += 1
end
§6.9.4 Iterator
和C/C++/Java不同,Ruby语言并不支持C/C++/Java中的For循环,但Ruby通
过迭代器来提供更为强大的功能。先看一个例子:
4.times do
puts "Hello!"
end
执行结果为:
84
Hello!
Hello!
Hello!
Hello!
除了times方法之外,整数还提供upto和downto两个方法,看以下例子,
0.upto(9) do |i|
print i, " "
end
执行结果为0 1 2 3 4 5 6 7 8 9 。
也可以使用Step方法,step第二个参数表示步长:
0.step(10, 2) do |i|
print i, " "
end
执行结果为:0 2 4 6 8 10 。
许多容器类,例如数组,提供了each方法依次遍历容器中的数据:
[1, 2, 3, 4, 5].each { |i| print i, " "}
执行结果为:1 2 3 4 5 。
如果一个类支持each方法,那么就可以使用Enumerable模块中的一些方法。
["apple", "orange", "banana", "watermelon"].grep(/an/) do |
fruit|
puts fruit
end
执行结果为:
orange
banana
§6.9.5 For..In
如果一个类提供了each方法,那么相应的,这个类的对象可以使用For..in循环。例如
85
Array类和Range类都有each方法:
for fruit in ["apple", "orange", "banana", "watermelon"]
print fruit, " "
end
执行结果为:apple orange banana watermelon 。
for i in 1..9
print i, " "
end
执行结果为:1 2 3 4 5 6 7 8 9 。
§6.9.6 Break,Redo,Next
Break,Redo和Next用来改变循环的流程。
§6.9.6.1 break
Break用来退出当前循环:
times = 0
loop do
times += 1
print "hello #{times}\n"
break if times > 2
end
执行结果为:
hello 1
hello 2
hello 3
与C/C++不同,如果循环有多重的话,break将退出最内层的循环。
outer = 0
loop do
86
outer += 1
inner = 0
loop do
inner += 1
print "Inner #{inner}\n"
break if inner > 1
end
print "Outer #{outer}\n"
break if outer > 1
end
执行结果为:
Inner 1
Inner 2
Outer 1
Inner 1
Inner 2
Outer 2
另一个与C/C++语言不同的地方是break只能从循环中退出,而不能从case中退出。
§6.9.6.2 redo
redo语句重新执行当前这一次循环。
count = 0
for i in 1..3
print "hello #{i}\n"
break if count == 1
if i > 1
count += 1
redo
end
end
执行结果为:
87
hello 1
hello 2
hello 2
上面的例子中,使用redo后,循环变量i的值还是2,可见redo语句重新执行了
这次循环。
和break语句类似,redo语句只对最内层的循环起作用。
3.times do
count = 0
for i in 1..3
print "hello #{i}\n"
break if count == 1
if i > 1
count += 1
redo
end
end
end
执行结果为:
hello 1
hello 2
hello 2
hello 1
hello 2
hello 2
hello 1
hello 2
hello 2
§6.9.6.3 next
Next类似C/C++中的continue语句,跳转到当前循环的头部,执行下一次循环。
loop do
88
times += 1
next if times == 2
print "hello #{times}\n"
break if times > 3
end
执行结果为:
hello 1
hello 3
hello 4
与break,redo类似,如果循环有多重,那么next只对最内侧的循环起作用。
outer = 0
loop do
outer += 1
inner = 0
loop do
inner += 1
next if inner == 1
print "Inner #{inner}\n"
break if inner > 1
end
print "Outer #{outer}\n"
break if outer > 1
end
执行结果为:
Inner 2
Outer 1
Inner 2
Outer 2
89
§6.9.7 Retry
上一节我们看到,可以使用redo重新执行当前这一次的循环,有时候,我们也需要
重新执行整个循环而不是仅仅执行当前这次,这时候我们可以用时retry。在迭代、块或
for语句中使用retry,意味着重启迭代器。同时迭代器的参数也将被重新计算。
一个示例如下,
for i in 1..5
retry if some_condition # 从 i == 1 开始重新执行
end
看一个完整可执行的例子:
count = 0
for i in 1..3
print "hello #{i}\n"
break if count == 1
if i > 1
count += 1
redo
end
end
执行结果为:
hello 1
hello 2
hello 1
hello 2
hello 1
90
第七章 方法
Ruby中的方法使用关键字def来定义。方法名应该以小写字母开始,如果你使用大
写字母开始,Ruby解释器会认为它是一个常量,这样可能会带来名称解析错误。
在定义方法时可以使用圆括号也可以不用。
def method1
puts "Hello World!"
end
def method2 arg1, arg2
puts "The arguments is: #{arg1}, #{arg2}"
end
一般的习惯是如果方法含有参数,那么就使用圆括号将参数括起来,否则的不要需要
圆括号。
def method2(arg1, arg2)
puts "The arguments is: #{arg1}, #{arg2}"
end
定义方法时可给方法默认参数,注意默认参数必须位于方法的尾部。
def method3(arg1=5, arg2=9)
puts "The arguments is: #{arg1}, #{arg2}"
end
def method3(arg1=5, arg2) #错误
方法的返回值为方法最后一个表达式的值,或者由return语句的返回的值。
和C/C++不同,Ruby中的方法总是从属于某一个对象。Ruby中没有全局函数。虽然
91
Ruby中可以象全局函数一样定义和使用方法,但是你应当明白,Ruby中的方法总是会从
属于某一个对象。
看到这里,细心的读者会提出一个问题,如果在顶层定义一个方法,那么这个方法属
于谁?
def meth
end
在本书中,我们多次说,Ruby是一种面向对象的语言,在Ruby中的一切都是对象。
那么meth方法从属于什么对象呢?我们看一个例子:
public #为什么使用public看后边的解释
def meth
puts self.class
end
puts self.class
self.meth
执行结果为:
Object
Object
在顶层,当我们定义方法时,将自动将我们定义的方法作为Object类的私有实例方
法。所以这些方法可以在任何地方调用。所以我们可以在任何地方使用Object类的任何方
法和Object类所包含的模块中的任何方法,例如Kernel模块中的方法在任何地方可以
随意使用。
上面的例子中,meth将作为Object类的私有方法,所以我们使用public改变它
的存取属性,否则self.meth将会产生无法访问私有对象的错误。
92
在Ruby语言中,方法是存放到Hash表中,而键值就是方法名。定义一个方法就是在
Hash表中添加一项的过程,所以,后定义的同名方法就会替换掉之前定义的方法。
def meth
puts "first"
end
meth
def meth
puts "second"
end
meth
执行结果为:
first
second
Ruby语言支持缺省参数,但不支持方法重载。方法重载会加重解释器语法解析的复杂
度,影响执行速度。C++的选择是二者都支持,而Java的选择刚好与Ruby相反,即支持
方法重载,而不支持缺省参数。
方法名可以以问号“?”,叹号“!”,等于号“=”结尾。这样的方法有约定的含义。以
问号结尾的方法返回布尔值,以叹号结尾的方法表示会改变调用者的内部数据,以等于号
结尾的方法表示可以作为左值。问号“?”,叹号“!”,等于号“=”是方法名的一部分。
(todo
例子)
当一个方法被调用时,运行时环境按照如下顺序搜索:
1、在当前对象的特殊方法中搜索
2、在当前对象类中定义的实例方法中搜索
93
3、在当前对象所包含的模块中搜索
4、在当前对象所在类的父类中搜索
5、在当前对象所在类的父类所包含的模块中搜索
6、继续4和5的过程,直到顶层Object类
§7.1 运算符重定义
Ruby支持重定义运算符,下表列出了Ruby支持重定义的运算符和他们默认的含义:
运算符默认含义
[] []= 元素索引和赋值
** 幂运算
! ~ 否定,求补
+
正,负,注意他们的方法名为+@,@
以便
和加减运算区分
* / % 乘法,除法,取余运算
+
加减运算
>> << 左移,右移
& | ^ 按位与,按位或,按位异或
<= < > >= 比较运算
=== Case表达式中的相等测试
<=> 范围测试
== != 正则表达式匹配相等和不等测试
=~ !~ 相等和不等测试
注意,!=和!~ 作为==和=~的否定形式,并没有相应的方法。他们经过语法分析后将
被转化为肯定形式调用。例如:
a != b实际是调用 !(a==b),同样,a !~ b将被转化为 !(a=~b)。
94
§7.2 变长参数
Ruby语言支持定义方法时,方法的参数数目不确定。只要给参数前加星号就表示参数
数目不定。所有的不定数目的参数被作为一个数组。
def varadd(*num)
sum = 0
num.each { |i| sum+=i }
puts sum
end
varadd()
varadd(1,2,3)
varadd(1,2,3,4,5,6)
执行结果为:
0
6
21
你也可以反过来使用星号,在调用方法时,如果一个类型为数组的参数前有星号作为前缀,
那么这个数组将被展开。
def meth1(arg1, arg2, arg3, arg4, arg5)
print
"Parameters:#{arg1},#{arg2},#{arg3},#{arg4},#{arg5} \n"
end
meth1(1, 2, 3, 4, 5)
meth1(1, 2, 3, *['4', '5'])
meth1(*(10..14).to_a)
执行结果为:
Parameters:1,2,3,4,5
Parameters:1,2,3,4,5
Parameters:10,11,12,13,14
95
§7.3 块调用
当调用一个方法时,可以在方法后连接一个块。在方法内可以使用yield执行连接的块。
def test_block1(arg1)
print arg1,"\n"
yield
end
test_block1("Test") { print "Hello World!" }
执行结果为:
Test
Hello World!
Kernel模块有一个block_given?的方法,可以判断方法后是否有块连接。
def test_block2(arg1)
if block_given?
print yield(arg1), "\n"
else
print arg1, "\n"
end
end
test_block2("no block")
test_block2("no block") {|s| s.sub(/no /, '') }
执行结果为:
no block
block
如果方法的最后一个参数以&开始,表示方法后连接的块会被转化为Proc对象,然后传
递给这个参数。
def meth1(count, &block)
value = 1
1.upto(count) do | i |
96
value = value * i
block.call(i, value)
end
end
meth1(4) do | i, f_i | puts "meth1(#{i}) = #{f_i}" end
执行结果为:
meth1(1) = 1
meth1(2) = 2
meth1(3) = 6
meth1(4) = 24
§7.4 方法返回值
每一个方法都有返回值,当然,你不一定要使用这个返回值。方法的返回值就是在调
用方法时方法内最后一个表达式执行的结果。你也可以使用return语句,这样方法
的返回值就是return语句的参数。Ruby语言习惯省略return语句,能少写的尽量
少写,这也是Ruby的哲学测。
def meth1()
"meth1"
end
def meth2(arg)
if arg == 0
"Zero"
else if arg > 0
"Positive"
else
"Negative"
end
end
使用return语句时返回值可以是多个,这时候返回值会被转化为一个数组,你可以
97
用多重赋值的形式来使用这个返回值。
def meth1
return "meth1", 6
end
a, b = meth1
98
第八章 模块
模块提供了一种组织常量,类和方法的手段。你可以使用模块来提供一个名字空间以避免
名字冲突,你也可以使用模块来提供mixin的功能。
§8.1 名字空间
当程序代码越来越多,工程越来越大,开发者不可避免的会将一些常用的代码以库或
别的形式重用。一般情况下,我们可以用类来组织代码,但有时候使用类组织代码并不是
十分合适。这样在一个大工程中,就有可能发生名字冲突。
例如,开发者A在文件a.rb中写了如下代码,用来输出自己代码文件的版本信息,
def print_version
# …
end
同一个项目中的另一个开发者B在文件b.rb中用同样的方法来实现同样的功能,
def print_version
# …
end
第三个开发者C 需要使用a.rb 和b.rb 中的一些方法,这样,当他使用
print_version 方法时,就产生了名字冲突,到底他调用的是哪一个文件中的
print_version方法呢?
99
我们可以使用模块机制来解决这样的名字冲突。定义一个模块相当于定义了一个名字
空间,名字空间内的元素在全局空间并不直接可见。
开发者A定义模块A_FILE,
module A_FILE
def print_version
# …
end
end
开发者B定义模块A_FILE,
module B_FILE
def print_version
# …
end
end
这样对于开发者C,可以这样使用print_version
require ‘A’
require ‘B’
A. print_version
B. print_version
类和模块的区别是,模块不能生成实例,而类不能被include。
§8.2 mixin
Mixin
的意思是混合插入、糅合,就像在冰淇淋中混合多种配料可以做成美味的混合
冰淇淋一样,在类中混合插入各种模块就可以添加相应的功能。模块还有另一个重要的作
用,可以使用模块来实现多继承,可以在类中包含模块,这样模块中的所有方法和类中其
他方法一样可以使用。
Matz坚信滥用多重继承会导致继承关系的混乱,因此Ruby中不允许使用多重继承。
100
同时为充分发挥继承功能的优势,Ruby支持两种继承关系:1.使用isa
语句的继承;2.使
用Mixin
来共享并继承模块中的功能。
module Debug
Define print_info
print "Class: #{self.class.name} Object ID: #{self.id}"
end
end
class A
include Debug
#...
end
class B
include Debug
#...
end
obj1 = A.new
obj2 = B.new
obj1.print_info
obj2.print_info
通过这样的手段我们可以实现多继承的功能,这样的模块我们称为mixin。
在Ruby中,Object,Class和Module是三个特殊的类。
Object
Module
Class
101
Class是一个Module,而Module是一个Object,所以说Class是一个Object,因此,
所有的数据都是Object。
§8.3 使用mixin
§8.3.1 Comparable
Comparable mixin提供了比较的功能。要使用Comparable mixin必须提供<=>
方法,<=>的返回值为1,0,+
1用来表示元素之间的小于,等于,大于的关系。
class Person
include Comparable
attr :age
def <=>(aPerson)
@age <=> aPerson.age
end
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
aPerson = Person.new("Tom", "male", 18)
bPerson = Person.new("Mike", "male", 10)
cPerson = Person.new("Henry", "male", 40)
puts aPerson > bPerson
puts aPerson < bPerson
puts aPerson >= bPerson
puts aPerson <= bPerson
puts aPerson == bPerson
102
puts aPerson.between?(bPerson, cPerson)
执行结果为:
true
false
true
false
false
true
§8.3.2 Enumerable
Enumerable mixin提供了遍历,查找和排序的功能。 要使用Enumerable mixin必须提供
each方法,标准做法是在each方法内对每一个元素使用yield操作。如果使用了Enumerable
mixin中的max,min,或sort,那么还必须提供<=>方法,用来实现元素之间的比较关系。
以下是一个使用Enumerable mixin的例子,IntegerFinder是一个查找字符串中整
数的类。
class IntegerFinder
include Enumerable
def initialize(aString)
@string = aString
end
def each
@string.scan(/[19]\
d*/) { |integer| yield integer }
end
end
aDigitFinder = IntegerFinder.new("This is 123, 234, 98 and 10")
aDigitFinder.collect {|i| print i, " "}
aArray = aDigitFinder.find_all {|i| i.to_i > 50 }
puts "\n", aArray.to_s
103
执行结果为:
123 234 98 10
12323498
Enumerable mixin 中含有许多与集合遍历查找相关的方法,许多标准类也使用了
Enumerable mixin,借助Enumerable mixin中的方法可以方便的实现一些强大的
功能,请看以下一些例子:
#察看数组中的所有单词的长度是否大于4
%w{ ant bear cat}.all? {|word| word.length >= 4} #=> false
#返回range中所有不符合条件的元素
(1..10).reject {|i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10]
#求5到10的和
#inject方法第一次会把Range中的前两个元素作为参数传递给sum和n,
#以后每次会把sum设置为块计算后的返回值。
(5..10).inject {|sum, n| sum + n } #=> 45
# 找出数组中最长的单词
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
§8.3.3 Singleton
在设计模式中,Singleton技术通常称为单件,是一种常见的设计技术,它保证在系统
的某个类在任一时刻最多只有一个实例在运行。你可以参见设计模式这本书获得有关单件
104
技术更详细的信息。在Ruby中,使用Singleton Mixin,
你可以很容易的实现单件类。
在单件类中不能使用new方法,因为在单件类中new方法的属性是私有的。需要使用
instance方法得到类的实例对象。
require 'singleton'
class SingletonClassTest
attr_accessor :data
include Singleton
end
# a = SingletonClassTest.new #错误,new方法为私有方法
a = SingletonClassTest.instance
b = SingletonClassTest.instance
puts a.inspect
puts b.inspect
a.data = 8
puts b.data
执行结果为:
#<SingletonClassTest:0x2ab9360>
#<SingletonClassTest:0x2ab9360>
8
可以看到,a和b其实指向同一个对象。
§8.4 Require, load和include
在Ruby中,可以使用load和require来包含另一个文件。每次运行到load时,load后的
文件会被载入并执行。
105
4.times do |i|
File.open("temp.rb","w") do |f|
f.puts "def test"
f.puts "#{i}"
f.puts "end"
end
load "temp.rb"
puts test
end
执行结果为:
0
1
2
3
在上面的小程序里load "temp.rb"执行了4次,每一次temp.rb文件都不同,所
以test方法执行后的结果也不同。
使用Load方法的这种特性可以实现一些强大的功能,例如:
 可以用来处理配置文件,在程序运行期间配置文件可以被动态改变。
 可以用来实现程序的无缝升级,在升级时你不需要重启程序,只要将所需要的代码重
新load。
Require和load不同,它只加载文件一次,即在第一次执行到require时载入,
以后碰到require同一文件时自动忽略。已经被加载的文件保存在$”中。另外,require
还可以用来加载二进制格式的库。Require可以使用绝对路径或相对路径,如果使用了相
对路径,那么系统会在$:变量包含的目录中搜寻。Require和load的另一个不同是当包
含文件是Ruby代码时,require可以不加后缀名。Require将当前所有加载的文件保存
在$"变量中。
注意,在当前版本中,$”是一个数组,保存着使用require已经加载过的文件。但
106
如果require使用不同的路径去包含同一个文件,这个文件就有可能被加载多次。
File.open("temp.rb","w") do |f|
f.puts "def test"
f.puts "1"
f.puts "end"
end
require "temp"
puts test
File.open("temp.rb","w") do |f|
f.puts "def test"
f.puts "2"
f.puts "end"
end
require "./temp"
puts test
执行结果为:
1
2
这样就违背了require只加载一次的初衷,一些人认为这是一个bug,这个问题在Ruby
的后续版本中可能被修复。所以,不要使用不同的路径去加载同一个文件。
require, load,include都是Kernel模块中的方法,他们的区别如下:
 require,load用于包含文件,include则用于包含的模块。
 require加载一次,load可加载多次。
 require加载Ruby代码文件时可以不加后缀名,load加载代码文件时必须加后缀
名。
 require一般情况下用于加载库文件,而load用于加载配置文件。
107
第9章异常
异常(Exception)顾名思义,指的是在程序处理过程中遇到不能处理的非预期条
件,它会打乱正常的执行流程。
许多种类的错误会触发异常,典型的异常例如整数除零,Ruby提供了一套处理
异常的机制。如果错误发生,方法将创建一个异常对象并把它抛出到运行时系统。创建
一个异常对象并将其交给运行时系统叫做抛出异常。异常对象包含了异常的信息,包
括异常类型,异常发生时的堆栈状态等。运行时系统负责找到一些代码处理这个错误。
当某个方法抛出一个异常后,运行时系统需要找到一些代码来处理这个异常。运
行时系统从发生异常的代码处开始,依次查找调用堆栈,直到找到相应的代码处理这
个异常,然后继续执行其下的代码。注意处理异常过后程序不会返回到异常抛出处继
续往下执行。
和传统错误管理技术相比,使用异常来管理错误具有如下优点:
 将正常处理逻辑与错误处理逻辑分开
 错误沿着调用堆栈向上传递,异常处理的代码简洁容易理解
 错误具有相应得类型,可以使用类型来区分不同的错误
§9.1 异常处理
Ruby处理异常的语法如下:
begin
108
# 正常处理流程代码
rescue ……
# 处理异常
ensure
# 这里的代码总会被执行
End
我们看一个例子,以下代码产生一个除零错误:
begin
numerator = 10
denominator = 0
value = numerator / denominator
rescue ZeroDivisionError => ex
puts "Error, divide zero"
ensure
puts "Finish the process!"
end
执行结果:
Error, divide zero
Finish the process!
在异常产生后,如果相应的rescue语句被匹配到,那么这个异常对象会被复制到
ex中。而ensure后的代码永远都会被执行到。
在Ruby中,异常类的基类是Exception,也就是说所有异常类都是Exception类
直接或间接的子类。Ruby预定义了一个层次结构用来处理常见的异常。
(todo
Ruby异常层次图)
在捕捉异常时,可以使用多个rescue语句:
begin
109
# 正常处理流程代码
rescue ExceptionClass1 => exception1
# 处理当发生ExceptionClass1类型的异常
rescue ExceptionClass2 => exception2
# 处理当发生ExceptionClas21类型的异常
rescue
# 处理其他异常
ensure
# 这里的代码总会被执行
end
注意,rescue不加参数默认是匹配StandardError类型的异常。
Ruby使用最先匹配的规则,如果一个异常和rescue语句匹配成功,那么接下来的
rescue语句都不会被匹配。在搜寻匹配的时候可以做从子类到父类的转换。
begin
number1 = number2
rescue SyntaxError, NameError => ex
print "The code doesn't compile: " + ex.class.to_s
rescue StandardError => ex
print "Error running script: " + ex.class.to_s
end
执行结果为:
The code doesn't compile: NameError
上面的例子中使用到number2,但是number2 并没有被定义,所以产生了一个
NameError。第一个rescue 语句被匹配到,于是放弃继续搜寻,直接执行相应的
rescue语句后的代码。
begin
number1 = number2
rescue StandardError => ex
110
print "Error running script: " + ex.class.to_s
rescue SyntaxError, NameError => ex
print "The code doesn't compile: " + ex.class.to_s
end
执行结果为:
Error running script: NameError
这个例子和第一个例子区别就是rescue 语句的顺序,从例子中可以看到,由于
StandardError 是NameError 和SyntaxError 的父类,而按顺序StandardError
会先被匹配到,所以会放弃继续搜寻,StandardError下面的代码会被执行。上面的例
子中,print "The code doesn't compile: " + ex.class.to_s永远不会被执
行到。
当抛出一个异常时,可以使用Ruby内置的异常类,也可以自定义自己的异常类。如
果使用自定义的异常类,那么这个异常类应该是StandardError类直接或间接的子类。
如果不这么做,那么默认情况下,这个异常不能正确地被捕捉。
在rescue处理逻辑之后可以加一个else子句,只有没有任何异常发生的时候程序
才会运行到那里,这种用法比较少见。
begin
# 正常处理流程代码
rescue ExceptionClass1 => exception1
# 处理当发生ExceptionClass1类型的异常
rescue ExceptionClass2 => exception2
# 处理当发生ExceptionClas21类型的异常
rescue
# 处理其他任何异常
else
111
# 没有任何异常
ensure
# 这里的代码总会被执行
end
可以使用raise显式抛出一个异常。raise后可以跟异常说明信息,这种情况下抛出
的异常为RuntimeError类型:
# 抛出一个RuntimeError类型的异常,异常说明信息"FTP Server down"
raise "FTP Server down"
可以使用raise抛出特定类型的异常:
# 抛出一个FTPServerError 类型的异常,异常说明信息" Server not
responding”
raise FTPServerError, "Server not responding"
还可以附加第三个参数表示调用栈的信息:
# 抛出一个FTPServerError 类型的异常,异常说明信息" Server not
responding”
#第三个参数为Kernel#caller方法的处理结果,用来指定调用栈的层次
raise FTPServerError, "Server not responding", caller
Kernel#caller方法可以返回一个数组包含了当前调用栈的信息,方法可以携带参
数,表示从调用栈内部开始,跳过堆栈的层次,默认值为1。
def meth1(skip)
meth2(skip)
end
def meth2(skip)
112
meth3(skip)
end
def meth3(skip)
caller(skip)
end
puts meth1(0)
puts meth1(1)
puts meth1(2)
puts meth1(3)
可以看到,puts meth1(0)的结果为:
test.rb:10:in `meth3'
test.rb:6:in `meth2'
test.rb:2:in `meth1'
puts meth1(1)的结果为:
test.rb:6:in `meth2'
test.rb:2:in `meth1'
puts meth1(2)的结果为:
test.rb:2:in `meth1'
puts meth1(3)的结果为空。
通过caller方法,当抛出异常时,我们可以指定调用栈的层次:
def meth1
meth2
end
def meth2
meth3
end
def meth3
begin
raise RuntimeError, "Test Error", caller[1..2]
113
rescue RuntimeError => ex
puts ex.backtrace
end
end
meth1
执行结果为:
test.rb:2:in `meth1'
上面的例子中,通过调用caller方法得到当前调用栈,然后通过caller[1..2]指
定异常发生时传递的调用堆栈信息,最后使用异常对象的backtrace方法来得到调用堆
栈。
使用raise不加参数表示重新抛出当前异常,如果当前没有异常,那么将抛出一个
RuntimeError类型的异常:
begin
numerator = 10
denominator = 0
value = numerator / denominator
rescue ZeroDivisionError => ex
puts "Error, divide zero"
raise #重新抛出当前异常
ensure
puts "Rethow the error!"
end
执行结果:
Error, divide zero
Finish the process!
如果没有任何相应的rescue语句处理异常,那么这个异常会交给运行时环境。运行
时环境会输入异常并中止程序的执行。
114
另外,全局变量“$!”保存了最近抛出的异常对象,而“$@”保存了最近发生的异常
发生异常的调用栈信息。
除了循环以外,还可以在rescue部分中使用retry。这时将从begin表达式开始重
新执行。使用retry可以在某处理过程成功之前,一直循环该处理过程。
begin
do_something # exception raised
rescue
# handles error
retry # restart from beginning
end
若在rescue 部分、迭代器块或for 语句之外使用retry 的话会引发
LocalJumpError异常。
§9.2 定义异常类
Exception类提供了堆栈和异常的描述信息,在自定义的异常类中,你可以添加额
外的信息。自定义的异常类应该是StandardError类直接或间接的子类。否则这个异常可
能不会正确地被捕捉。
看下面的例子,以下代码位于MyExceptionTest.rb中。
class MyException < RuntimeError
end
begin
raise MyException, "MyException Testing..."
rescue MyException => ex
puts ex.to_s
115
puts ex.backtrace.join("\n")
ensure
puts "Finish the process!"
end
执行结果为:
MyException Testing...
MyException.rb:5
Finish the process!
§9.3 catch和throw
使用raise,rescue用来处理出错的情况,同时也可以使处理流程从很深的嵌套结
构中跳出。catch和throw也能使处理流程从很深的嵌套结构中跳出。
Catch定义了一个代码块,并给这个代码块一个名字作为标注。当Ruby遇到一个
throw时会展开调用堆栈来查找相应的catch块,当找到时,Ruby在相应得catch处
展开堆栈并中止块。
def method1(n)
puts n
throw :done if n <= 0
method1(n1)
end
catch(:done) {
method1(3)
puts "Can not reach here!"
}
puts "Reach here!"
执行结果:
3
2
1
116
0
Reach here!
上面的例子中,我们首先用“catch(:done)”标注了一个块,当执行到
“throw :done”时,中止当前“catch(:done)”所标注的块,处理流程继续向下处理。
在catch/throw 中,当碰到throw 时,Ruby 会展开调用堆栈来查找匹配的
catch,当找到后,堆栈会被展开,catch块剩余的代码不会被执行。
Ruby中没有goto 语句,但你可以使用catch/throw或异常来实现goto的功能。
第10章多任务处理
当你要同时处理多件任务时Ruby提供了两种基本方式,一种方式是在一个程序
协调需要同时执行的任务,使用多线程处理需要同时执行的操作。另一种方式是将需
要同时执行的操作分散到多个程序中使用多进程。
按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。
一般来说,我们把正在计算机中执行的程序叫做“进程”(Process),而所谓“线程
”(Thread),是“进程”中某个单一顺序的控制流。
线程之间独立运行,每个线程有它自己的堆栈、自己的程序计数器和自己的局部
变量。但是与进程相比,线程之间的隔离程度要小。它们彼此共用变量,也有可能会同
时操作一片内存。
一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如CPU、
内存、文件等等),而将线程分配到某个CPU上执行。一个进程可以拥有多个线程,
此时,如果进程运行在多处理器的机器上,它就可以同时使用多个CPU来执行各个
117
线程,达到最大程度的并行,以提高效率;同时,即使是在单CPU的机器上,采用
多线程模型来设计程序,可以使设计更简洁、功能更完备,程序的执行效率也更高。例
如采用多个线程响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进
程模型来实现,而与后者相比,线程的上下文切换开销就要小多了。
在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器
以及减小上下文切换开销。
§10.1 多线程处理
Ruby的多线程是用户级多线程,这样使得Ruby的多线程移植非常容易,你并不需关
心具体的操作系统;这样做也使线程容易控制,程序不容易产生死锁这类严重的线程问题。
但是同时,由于Ruby的多线程并不是真正意义上的操作系统级多线程,不管代码使用了
多少个Thread类的实例,都只会在启动解释器这一个进程内执行,由Ruby解释器进行具
体的线程切换管理,其效率要低于由操作系统管理线程的效率,且不能使用多个CPU。
在Ruby中同时做多件事最简单的方式就是使用Thread类,Thread类提供了一种高效
和轻量级的手段来同时处理多件任务。
Thread类由Ruby解释器具体实现,提供了一种同时处理多个任务的方法, Thread类
实现的并不是操作系统级多线程。
Ruby多线程的优点和缺点同样明显,缺点是效率不如操作系统级多线程,不能使用
多个CPU,但其优点也很明显,即可移植性很高。这就需要设计人员综合考虑。
118
§10.1.1 线程创建
我们可以使用Thread.new方法去创建一个线程,可以随后代码块中列出线程执行
的代码:
x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
执行结果为:
ab
上面的示例程序中使用Thread.new创建了二个线程,线程随即开始运行。但是运行
结果很奇怪,为什么程序运行结果是“ab”呢?我们预期的执行结果应该是 “abxyzc”。
当Ruby程序执行完毕的时候,他会杀掉所有的线程,不管其它的线程的运行状态如
何。如果没有使用join方法,那么主程序执行完毕后会把所有没有执行完毕的线程杀掉。
上面的实例程序中,当主程序运行完毕时,两个线程都没有运行结束就被中止掉了。
我们可以使用join方法来让主程序等待某个特定线程结束,对每一个线程使用join方法,
可以确保在程序结束前所有的线程可以运行完毕。
x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
x.join
a.join
我们使用Thread.new方法创建两个新的线程并开始运行, 然后使用join方法等待
线程结束。执行结果为:
abxyzc
可以看到通过使用join方法等待线程结束,程序运行结果和我们预期结果相符。
119
另一个例子:
x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
a = Thread.new { sleep 0.1; print "a"; print "b"; sleep 0.2;
print "c" }
执行没有任何输出,因为主程序执行完毕杀死两个线程的时候这两个线程没有运行到
输出语句。
也可以给join方法添加时间用来指明最大等待时间。如果超时join返回nil。
x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
a = Thread.new { sleep 0.1; print "a"; print "b"; sleep 10;
print "c" }
x.join(5)
a.join(5)
执行结果为:
abxyz
上面的例子中对于每一个线程的最大等待时间是5秒,由于a线程需要执行10秒以上,
所以a线程没有运行完毕程序就将返回。
§10.1.2 线程操作
可以使用Thread.current方法来访问当前线程,也可以使用Thread.list方法
列出所有线程;可以使用Thread#status和Thread#alive?方法得到线程的状态;也
可以使用Thread#priority方法得到线程的优先级和使用Thread#priority=方法来
调整线程的优先级。
test_thread = Thread.new {}
test_thread.join
120
Thread.current.alive? => true
Test_thread.alive? => false
上面的例子中,使用alive?方法得线程的状态,由于主线程并没有执行完毕所以状
态为true,由于Test_thread已经执行完毕所以返回false。
Thread.new { sleep(200) }
Thread.new { 1000000.times {|i| i*i } }
Thread.new { Thread.stop }
Thread.list.each {|t| p t}
执行结果为:
#<Thread:0x2938168 sleep>
#<Thread:0x2938228 run>
#<Thread:0x29382b8 sleep>
#<Thread:0x2955628 run>
上面的例子中创建了三个线程,然后列出所有的线程,可以看到,列出的线程数目是
四个而不是三个,因为除了创建的三个线程之外还有主线程。
number1 = 0
number2 = 0
thr1 = Thread.new { loop { number1 += 1 } }
print "th1:", thr1.priority, "\n"
thr1.priority = 1
thr2 = Thread.new { loop { number2 += 1 } }
print "th2:", thr2.priority, "\n"
thr2.priority = 2
sleep 1
Thread.critical = 1
puts number1
puts number2
输出结果为:
th1:0
th2:0
174100
98
121
上面的例子中使用priority方法得到线程的优先级,使用priority=方法设置线
程的优先级。县城的优先级默认为0。可以看到同样的代码,优先级较高的执行的要快。
我们可以使用critical 方法和critical=方法得到和设置全局线程临界区
(Thread.critical)的值,如果这个值为true,那么所有存在的线程将停止运行。
一个Thread可以访问自己作用域内的所有数据,但如果有需要在某个线程内访问其
他线程的数据应该怎么做呢?Thread类提供了线程数据互相访问的方法,你可以简单的
把一个线程作为一个Hash表,可以在任何线程内使用[]=写入数据,使用[]读出数据。
athr = Thread.new { Thread.current["name"] = "Thread A";
Thread.stop }
bthr = Thread.new { Thread.current["name"] = "Thread B";
Thread.stop }
cthr = Thread.new { Thread.current["name"] = "Thread C";
Thread.stop }
Thread.list.each {|x| puts "#{x.inspect}: #{x["name"]}" }
可以看到,把线程作为一个Hash表,使用[]和[]=方法,我们实现了线程之间的数
据共享。
§10.1.3 线程和异常
当一个线程运行时如果发生异常会怎样?这和abort_on_exception 标志和解释器的
debug标志有关,abort_on_exception默认被设置为false,这时候发生异常的那个线程会被
杀死,其他线程继续运行。只有当你对发生异常的线程使用join操作时你才会察觉到有异
常发生。
a = Thread.new { raise "Error A" }
122
b = Thread.new { puts "Thead B" }
c = Thread.new { puts "Thead C" }
b.join
c.join
123
执行结果为:
Thead B
Thead C
a = Thread.new { raise "Error A" }
b = Thread.new { puts "Thead B" }
c = Thread.new { puts "Thead C" }
a.join
b.join
c.join
执行结果为:
Thead B
Thead C
test.rb:1: Error A (RuntimeError)
from test.rb:4:in `join'
from test.rb:4
我们也可以在使用join时捕获这个异常:
threads = []
threads[0] = Thread.new { raise "Error A" }
threads[1] = Thread.new { puts "Thead B" }
threads[2] = Thread.new { puts "Thead C" }
threads.each do |t|
begin
t.join
rescue RuntimeError => e
puts "#{e.message}"
end
end
执行结果为:
Thead B
Thead C
Error A
然而设置了abort_on_exception标志或者使用d
打开了debug开关,如果抛出
了未被捕获的异常,程序将结束,所有的线程都将被终止。
124
Thread.abort_on_exception = true
threads = []
threads[0] = Thread.new { raise "Error A" }
threads[1] = Thread.new { puts "Thead B" }
threads[2] = Thread.new { puts "Thead C" }
threads.each do |t|
begin
t.join
rescue RuntimeError => e
puts "#{e.message}"
end
end
执行结果为:
test.rb:3: Error A (RuntimeError)
from test.rb:3:in `initialize'
from test.rb:3:in `new'
from test.rb:3
§10.1.4 线程调度
Thread类提供了一系列的方法用来控制线程调度,Thread#run方法用来运行一个
线程;Thread#stop方法停止线程的运行。
Thread#pass方法取消运行当前线程,继续其他线程的运行;Thread#join方法
将当前线程挂起直到指定线程运行结束;Thread#value方法和Thread#join类似,将
线程挂起直到指定线程运行结束并取得返回值。
a = Thread.new {
print "a";
Thread.pass;
print "b";
Thread.pass;
print "c"
}
125
b = Thread.new {
print "x";
Thread.pass;
print "y";
Thread.pass;
print "z"
}
a.join
b.join
执行结果为:
axbycz
上面的例子中,我们使用pass方法放弃当前的控制权,继续下一个线程的运行。
a = Thread.new { print "a"; Thread.stop; print "c" }
Thread.pass
print "b"
a.run
a.join
执行结果为:
abc
上面的例子中,线程a开始运行后先输出“a”,然后使用stop方法停止运行;主线
程使用pass方法放弃放弃当前的控制权,保证“a”最先输出。然后我们使用run方法重
新运行线程a,所以输出结果为“abc”。
a = Thread.new { 4 + 5 }
puts a.value
执行结果为:
9
上面的例子中,我们是用value方法等待线程结束并打印出线程执行的返回值,线
126
程执行的返回值就是线程执行的最后一个语句的值。
§10.1.5 线程同步
因为线程共享内存空间,所以可以使用普通的变量完成线程间的数据交换工作。如果
有多个线程同时工作,请确保它们之间不存在互相等待以到达某一点或完成的情况。如果
操作错误,可能会导致死锁状态,两个线程都无法完成,因为它们都在相互等待。
使用线程的另一个常见问题是竞争状态。如果一个线程正在将数据写入文件,而另一
个线程正在从该文件中读取数据,如果读取线程快于写入线程,则将返回无法预料的结果。
这种情况称为竞争状态。
有时候在运行程序中,一些你认为不需要同步的地方也会出现资源同步的问题:
class Counter
attr_reader :number
def initialize
@number = 0
end
def plus
@number += 1
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.plus } }
t2 = Thread.new { 10000.times { c.plus } }
t1.join
t2.join
puts c.number
执行结果为:
14228
127
这里或许大家会奇怪,为什么number的值不是20000呢?
原因在于这一句:
@number += 1
这一行在解释器内部会分解为多个操作,
得到number的值
将number的值加一
保存number的值
在两个线程运行过程中,因为时机问题,在一个线程读取number的数值后还没来得
及进行赋值的时候,另一个线程有可能已经改变了number的数值。这样多个进程同时访
问共享数据造成了数据混乱从而与我们预想的结果不符。
§10.1.5.2 Monitor
Monitor是一种资源互斥访问的解决方案。它提供了一种机制,以供不同线程互斥访
问指定的共享资源。
我们可以通过使用继承Monitor类解决上一节的问题。
require 'monitor'
class Counter < Monitor
attr_reader :number
def initialize
@number = 0
super #初始化父类数据
end
def plus
synchronize do
128
@number += 1
end
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.plus } }
t2 = Thread.new { 10000.times { c.plus } }
t1.join
t2.join
puts c.number
执行结果为:
20000
可以看到,继承Monitor 类后,我们可以使用synchronize 方法,对于一个
Monitor类的实例,同时只能有一个线程执行synchronize代码块的操作。代码的执行
结果为“20000”,和我们期望的一致。
如果不想使Monitor成为自己类的父类,也可以使用MonitorMixin模块。
require 'monitor'
class Counter
include MonitorMixin
attr_reader :number
def initialize
@number = 0
super
end
def plus
synchronize do
@number += 1
129
end
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.plus } }
t2 = Thread.new { 10000.times { c.plus } }
t1.join
t2.join
puts c.number
执行结果为:
20000
也可以单独使用Monitor类的实例对象来完成同步操作。
require 'monitor'
class Counter
attr_reader :number
def initialize
@number = 0
super
end
def plus
@number += 1
end
end
c = Counter.new
lock = Monitor.new
t1 = Thread.new { 10000.times { lock.synchronize{ c.plus } } }
t2 = Thread.new { 10000.times { lock.synchronize{ c.plus } } }
t1.join
t2.join
130
puts c.number
执行结果为:
20000
也可以将使用extend方法将Monitor模块的方法引入完成同步操作。
require 'monitor'
class Counter
attr_reader :number
def initialize
@number = 0
super
end
def plus
@number += 1
end
end
c = Counter.new
c.extend(MonitorMixin)
t1 = Thread.new { 10000.times { c.synchronize{ c.plus } } }
t2 = Thread.new { 10000.times { c.synchronize{ c.plus } } }
t1.join
t2.join
puts c.number
§10.1.5.2 Mutex
Mutex是mutualexclusion
lock(互斥锁)的简称。它实现了一种简单的信号量
机制,用来协调并发线程对共享数据的访问。
131
在多线程并行访问共享数据时,可以使用下列代码(m是Mutex的实例)。
begin
m.lock
# 访问受m保护的共享数据
ensure
m.unlock
end
Mutex类有个synchronize方法可以简化这一过程。
m.synchronize {
# 访问受m保护的共享数据
}
若对Mutex加锁时发现已经处于锁定状态时,线程会挂起直到解锁为止。
我们修改一下上一节的例子,
require 'thread'
class Counter
attr_reader :number
def initialize
@number = 0
end
def plus
@number += 1
end
end
c = Counter.new
m = Mutex.new
t1 = Thread.new {
m.synchronize {
10000.times { c.plus }
}
132
}
t2 = Thread.new {
m.synchronize {
10000.times { c.plus }
}
}
t1.join
t2.join
puts c.number
输出结果为:
20000
从上一节我们可以知道,若此程序中不使用Mutex加以保护的话,因为时机问题,在
一个线程读取number的数值后还没来得及进行赋值的时候,另一个线程可能已经改变了
number的数值。这样,通过Mutex互斥锁,我们实现了对共享数据的正确使用。
Mutex主要方法如下:
new 生成新的互斥锁
lock 若已经处于加锁状态则会一直等待下去直到解锁
unlock 执行解锁操作,若有其它等锁的线程则会将他们唤醒
synchronize 执行从获得锁到解锁全过程的迭代器
try_lock 尝试执行加锁操作,若已处于加锁状态,则返回false且不会挂起
locked? 测试Mutex对象是否处于加锁状态
§10.1.5.3 Queue
Queue就像一条读写数据的管道。提供数据的线程在一边写入数据,而读取数据的线
程则在另一边读出数据。若Queue中没有可供读取的数据时,读取数据的线程会挂起等待
数据的到来。
下面是一个使用Queue的简单例子:
133
require "thread"
q = Queue.new
th = Thread.start {
while line = q.pop
print line
end
}
while gets
q.push $_
end
q.push nil# 终止标记
th.join
本程序中,一个线程读入一行之后,另一个线程就输出它。若把第3行改成数组,即
“q = []”后,线程间失去同步,则程序无法正确运作。
Queue有下列方法:
new 生成新的Queue
empty 测试Queue是否为空,若为空则返回真
push 将数据压入Queue
pop 尝试从Queue中取数据,如果Queue为空当参数为真则引发一个异常,否
则挂起线程
enq 含义与push相同
deq 含义与pop相同
clear 清空Queue
length 返回Queue中的元素个数
size 含义与length相同
下面的例子使用Queue来解决生产者
消费者问题。生产者
消费者问题是一个经典的进
程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。生产者线程生产
物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中拿走
134
产品。生产者线程在缓冲区满时必须等待,直到缓冲区有空间才继续生产。消费者线程在缓
冲区空时必须等待,直到缓冲区中有产品才能继续读取。在这个问题上需要考虑缓冲区满
和缓冲区空的情况以及竞争条件。
require 'thread'
queue = Queue.new
consumers = Thread.new do
5.times do |i|
obj = queue.deq
print "consumer: #{i}\n"
sleep(rand(0.05))
end
end
producers = Thread.new do
5.times do |i|
sleep(0.1)
print "producer: #{i}\n"
queue.enq("Item #{i}")
end
end
producers.join
consumers.join
§10.2 多进程处理
Thread类实现的并不是真正意义上的操作系统级多线程,但是有时候,你需要将一个
任务分解为几个进程去执行,或者可能你需要运行的进程并不是Ruby语言编写,对于这
种情况,Ruby语言也有办法处理,Ruby有几种方法创建真正的操作系统级的进程。
135
§10.2.1 进程创建
§10.2.1.1 system方法和反引号
可以使用反引号或system去执行一个命令。
system(“tar czf samples.tar.gz .”)
result = `date`
使用Kernel.system方法去执行一个外部命令时,这个外部命令将在一个子进程中
被执行。如果命令无法执行返回false,否则返回true。命令执行的返回值保存在全局变量$?
中。
使用system方法执行外部命令,外部命令的结果输出将和程序的结果输出在一起。使
用反引号则不同,执行的返回值为就是外部命令执行的输出结果。
result = `date`
result #=> Thu Jan 4 22:45:49 CST 2007
§10.2.1.2 popen方法
很多时候,我们需要将输入数据发送给外部命令,然后取回执行结果。可以使用
IO.popen方法完成这样的功能。
IO.popen ("date") { |f| puts f.gets } 
执行结果为:
Thu Jan 4 22:55:19 CST 2007
另一个例子:
command1 = IO.popen("dir", "w+")
command1.puts "."
command1.close_write
puts command1.gets
136
§10.2.1.3 fork方法
也可以使用fork方法产生一个新的子进程。产生子进程后原有进程继续向下执行。
fork do
3.times {|i| puts "Child: #{i}" }
end
3.times {|i| puts "Parent: #{i}" }
执行结果为:
Child: 0
Parent: 0
Child: 1
Parent: 1
Child: 2
Parent: 2
fork方法调用时返回值如果是nil表示子进程ID,否则表示的是父进程的ID。可以使
用Process.wait方法来等待子进程执行结束,Process.wait方法返回所在进程的ID。进程的
退出状态保存在$?中。
fork{
sleep(3)
}
Process.wait
注意,如果要使用fork方法,那么在Ruby解释器所在的操作系统必须支持fork系统
调用。例如在Window下fork方法就无法使用。
第11章基本I/O操作
I/O是Input/Output的缩写, 类IO是所有IO处理的基类,定义了I/O操作的接口。I/O
流可以是单向也可以是双向。
137
IO类实用Unix文件描述符的概念,使用整数代表打开的文件。0代表标准输入,1代
表标准输出,2代表标准错误。
IO端口可以以多种模式打开,以下表中列出了所有模式:
模式含义
r 只读,从文件头开始(默认模式)
r+ 读写,从文件头开始
w 只写,文件不存在则创建文件,文件存在则清空内容
w+ 读写,文件不存在则创建文件,文件存在则清空内容
a 只写,文件不存在则创建文件,文件存在则在尾部添加
a+ 读写,文件不存在则创建文件,文件存在则在尾部添加
b (只用于DOS/Windows)二进制文件,可以和上述模式一同出现
§11.1 使用Kernel模块处理I/O操作
在Ruby中你可以使用多种方法操作I/O。在Kernel模块中已经实现了一系列I/O相关
的方法gets, open, print, printf, putc, puts, readline, readlines, 和test方法,使用他们可以方便快
捷的写出程序。
§11.2文件处理
我们使用File类来操作文件,可以通过File.new来创建一个File类的实例并打开这个
文件。
file = File.new("testfile", "r")
# ... process the file
file.close
testfile是想要操作的文件名,”r”说明了文件的操作模式为读取。可以使用”w”
表示写入,”rw”表示读写。
138
最后记得关闭打开的文件,确保所有被缓冲的数据被写入文件,所有相关的资源被释
放。
我们也可以使用File.open来打开文件,open和new的不同是open可以使用其后
的代码块而new方法则返回一个File类的实例。
File.open("testfile", "r") do |file|
# ... process the file
end
open操作的另一个优点是处理了异常,如果处理一个文件发生错误抛出了
异常的话,那么open操作会自动关闭这个文件,下面是open操作的大致实现:
class File
def File.open(*args)
result = f = File.new(*args)
if block_given?
begin
result = yield f
ensure
f.close
end
end
return result
end
end
对于文件的路径,Ruby 会在不同的操作系统间作转换。例如,在Windows 下,
/ruby/sample/test.rb会被转化为\ruby\sample\test.rb。当你使用字符串表示
一个Windows下的文件时,请记住使用反斜线先转义:
“c:\\ruby\\sample\\test.rb”
你也可以使用File::SEPARATOR表示不同系操作统的路径分割符。
139
§11.3 StringIO
StringIO 是与IO 相同接口的字符串类。StringIO 操作像其它的I/O 对象,但
StringIO是对字符串进行读和写。如果打开一个StringIO对象用于读,需要提供给它
源字符串,然后在StringIO对象上进行所有读操作并返回操作结果。同样,当对一个
StringIO对象执行写操作时,需要传递给它一个要被填充的目的字符串。
#创建一个StringIO对象
io = StringIO.new("abc")
io.getc
#从io中读取一个字符,返回ASCII码
p io.getc =>98
#当前的位置
p io.pos => 2
p io.size => 3
io << "bar"
p io.size => 5
io.rewind
p io.gets => "abbar"
require 'stringio'
ip = StringIO.new("This\nis\na\ndog!")
op = StringIO.new("", "w")
ip.each_line do |line|
op.print "...", line
end
print op.string
输出结果为:
140
...This
...is
...a
...dog!
§11.4 Socket
使用Ruby 处理网络十分灵活, Ruby 提供了一系列的库来支持
TCP,UDP,HTTP,SOCKS,Unix套接字和各种各样的网络协议。所有的Socket的都是
IO直接或间接的子类。
(todo
图)
require 'socket'
socket = UDPSocket.new
socket.bind("127.0.0.1", 12345)
loop do
msg, sender = socket.recvfrom(100)
host = sender[3]
puts "#{Time.now}: #{host} '#{msg}'"
end
require 'socket'
log = UDPSocket.new
log.connect("127.0.0.1", 12121)
log.print "Up and Running!"
log.print "Done!"
141
第十二章 反射和对象空间
§12.1 ObjectSpace模块
使用ObjectSpace模块可以察看当前系统中的所有对象,也可以和垃圾回收器交互。
include ObjectSpace
a = 123
b = 456.789
each_object(Numeric) { |x| p x }
执行结果如下:
456.789
123.456
2.71828182845905
3.14159265358979
2.22044604925031e016
1.79769313486232e+308
2.2250738585072e308
100.0
86400000000
30.6001
30.6001
365.25
365.25
122.1
4.0
36524.25
1867216.25
30.6001
365.25
4.0
100.0
142
each_object列举系统中的所有指定的对象并返回对象的数目,上述代码列出了系统中
所有的数值型数据,除了我们的数据外还有一些系统预定义的常量。输出结果在你的机器
上或许会稍有不同。
注意each_ojbect方法并不列举Fixnum, Symbol, true, false和nil型的数据。
a = 102.7
b = 95 # Won't be returned
c = 12345678987654321
count = ObjectSpace.each_object(Numeric) {|x| p x }
puts "Total count: #{count}"
执行结果为:
12345678987654321
102.7
2.71828182845905
3.14159265358979
2.22044604925031e016
1.79769313486232e+308
2.2250738585072e308
100.0
86400000000
30.6001
30.6001
365.25
365.25
122.1
4.0
36524.25
1867216.25
30.6001
365.25
4.0
100.0
Total count: 21
使用ObjectSpace模块也可以给对象提供析构方法,这个方法在对象被垃圾回收器摧毁时
143
被调用。
include ObjectSpace
a = "A"
b = "B"
c = "C"
define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
define_finalizer(a, proc {|id| puts "Finalizer two on #{id}" })
define_finalizer(b, proc {|id| puts "Finalizer three on
#{id}" })
执行结果为:
Finalizer three on 537763470
Finalizer one on 537763480
Finalizer two on 537763480
§12.2 察看类和对象的状态
当找到一个感兴趣的对象,如何察看它的内部状态?毕竟,我们往往对它的内部状态
更感兴趣。在静态语言中,对象的类型取决于对象的所属类,察看对象所属的类就可以得
到对象支持的方法。和静态语言不同,Ruby支持更为自由的对象。
(todo
解释Ruby对象的自由性)
可以查看一个对象支持那些方法:
str = “123” #创建一个String对象
list = str.methods
list[0..3] => ["lstrip!", "to_sym", "scan",
"instance_variables"]
可以查看某个对象是否支持特定的方法:
144
str.respond_to?(“==”) => true
str.respond_to?(“scan”) => true
str.respond_to?(“xyz”) => false
也可以查看对象的ID,对象从属的类,对象和类的关系:
num = 6
num.id => 13
num.class => Fixnum
num.kind_of? Fixnum => true
num.kind_of? Numeric => true
num.instance_of? Fixnum => true
num.instance_of? Numeric => false
可以查看class之间的从属关系,可以通过class#superclass察看类的父类,可
以通过Module#ancestors列出所有的父类和模块。
Fixnum.superclass=> Integer
Fixnum.ancestors => [Fixnum, Integer, Precision, Numeric,
Comparable, Object, PP::ObjectMixin, Fox, Kernel]
我们还可以查看对象中方法的存取属性,可以查看对象中的常量和局部变量。
class InfoTest
@@static_variable = 101
CONST = "This is a constant!"
private
def private_method
end
protected
def protected_method
end
public
def public_method
@instance_variable = 100
i = 1
j = 2
local_variables
end
145
def InfoTest.class_method
end
end
我们可以通过以下方法访问InfoTest类的信息:
InfoTest.private_instance_methods(false) => private_method
InfoTest.protected_instance_methods(false) => protected_method
InfoTest.public_instance_methods(false) => public_method
InfoTest.singleton_methods(false) => class_method
InfoTest.class_variables => @@static_variable
InfoTest.constants => CONST
InfoTest.constants InfoTest.
superclass.constants => CONST
146
这里我们可以通过
private_instance_methods,protected_instance_methods,public_instan
ce_methods,singleton_methods方法分别访问类的私有方法,保护方法,公有方法
和类方法。参数false表示不列出父类的方法,如果参数为true那么父类的相关方法也
会被列出。class_variables可以访问类的类变量,constants类出类的包括父类的所
有常量,所以InfoTest.constantsInfoTest.
superclass.constants表示只列出
InfoTest类的常量。这些方法都是在Module类中作的定义,Module类包含许多可以访
问类详细信息的方法。
InfoTest是Class类的实例,Class类的继承关系如下,
Class >
Module >
Object
所以InfoTest类可以使用这些方法访问类的详细信息。
info = InfoTest.new
info.instance_variables => []
info.public_method
info.instance_variables => [@instance_variable]
instance_variables方法在Object类作的定义,返回当前实例对象的中的变量,
从上面的代码可以看出,只有当被使用到才会创建相应的变量,从这里也可以看出Ruby
的动态性。
§12.3 动态方法调用
在Ruby中,有多种方法可以实现方法的动态调用。
147
§12.3.1 使用send方法
第一种实现动态方法调用是使用send方法,send方法在Object类中定义,方法的第
一个参数是一个符号用来表示所要调用的方法,后面则是所调用方法需要的参数。
"This is a dog1".send(:length) => 14
上面的代码中通过send方法去对一个字符串执行length操作,返回字符串的长度。
class TestClass
def hello(*args)
"Hello " + args.join(' ')
end
end
a = TestClass.new
puts a.send :hello, "This", "is", "a", "dog!"
执行结果为:
Hello This is a dog!
§12.3.2 使用Method类和UnboundMethod类
另一种实现动态方法调用是使用Object 类的method 方法,这个方法返回一个
Method类的对象。我们可以使用call方法来执行方法调用。
test1 = "This is a dog1".method(:length)
test1.call => 14
class Test
def initialize(var)
@var = var
end
def hello()
"Hello, @var = #{@var}"
148
end
end
k = Test.new(10)
m = k.method(:hello)
m.call #=> "Hello, @iv = 99"
l = Test.new('Grant')
m = l.method("hello")
m.call #=> "Hello, @iv = Fred"
可以在使用对象的任何地方使用method对象,当调用call方法时,参数所指明的
方法会被执行,这种行为有些像C语言中的函数指针。你也可以把method对象作为一个
迭代器使用。
def square(a)
a*a
end
mObj = method(:square)
[1, 2, 3, 4].collect(&mObj) => [1 4 9 16]
Method 对象都是和某一特定对象绑定的,也就是说你需要通过某一对象使用
Method对象。你也可以通过UnboundMethod类创建对象,然后再把它绑定到某个具体
的对象中。如果UnboundMethod对象调用时尚未绑定,则会引发异常。
class Double
def get_value
2 * @side
end
def initialize(side)
@side = side
end
end
a = Double.instance_method(:get_value) #返回一个UnboundMethod 对

149
s = Double.new(50)
b = a.bind(s)
puts b.call
执行结果为:
100
看下面一个更具体的例子:
class CommandInterpreter
def do_2() print "This is 2\n"; end
def do_1() print "This is 1\n"; end
def do_4() print "This is 4\n"; end
def do_3() print "This is 3\n"; end
Dispatcher = {
?2 => instance_method(:do_2),
?1 => instance_method(:do_1),
?4 => instance_method(:do_4),
?3 => instance_method(:do_3)
}
def interpret(string)
string.each_byte {|i| Dispatcher[i].bind(self).call }
end
end
interpreter = CommandInterpreter.new
interpreter.interpret('1234')
执行结果为:
This is 1
This is 2
This is 3
This is 4
150
§12.3.3 使用eval方法
我们还可以使用eval方法实现方法动态调用。eval方法在Kernel模块中定义,有
多种变体如class_eval,module_eval,instance_eval等。Eval方法将分析其后
的字符串参数并把这个字符串参数作为Ruby代码执行。
str = "Hello"
eval "str + ' World!'" => Hello World!
sentence = %q{"This is a test!".length}
eval sentence => 15
当我们在使用eval方法时,我们可以通过eval方法的第二个参数指明eval所运
行代码的上下文环境,这个参数可以是Binding类对象或Proc类对象。Binding类封装
了代码在某一环境运行的上下文,可以供以后使用。
class BindingTest
def initialize(n)
@value = n
end
def getBinding
return binding() #使用Kernel#binding 方法返回一个Binding 对

end
end
obj1 = BindingTest.new(10)
binding1 = obj1.getBinding
obj2 = BindingTest.new("Binding Test")
binding2 = obj2.getBinding
puts eval("@value", binding1) #=> 10
puts eval("@value", binding2) #=> Binding Test
puts eval("@value") #=> nil
151
可以看到上述代码中,@value 在binding1 所指明的上下文环境中值为10,在
binding2所指明的上下文环境中值为Binding Test。当eval方法不提供binding参
数时,在当前上下文环境中@value并未定义,值为nil。
§12.3.4 性能
从上面我们可以看到,我们有多种实现动态方法调用的方式,但是请注意,在这多种
方式中,使用eval方法最慢。Ruby提供了一个Benchmark模块可以用来报告代码的执行时
间,衡量代码的性能。以下是使用Benchmark模块来显示不同动态方法调用的执行时间:
require 'benchmark'
test_string = "This is a test!"
meth = test_string.method(:length)
num = 100000
Benchmark.bm(12) do |x|
x.report("call") { num.times { meth.call } }
x.report("send") { num.times { test_string.send(:length) } }
x.report("eval") { num.times { eval "test_string.length" } }
end
执行结果为:
all 0.090000 0.000000 0.090000 ( 0.090000)
send 0.090000 0.000000 0.090000 ( 0.090000)
eval 1.352000 0.000000 1.352000 ( 1.352000)
可以看到,使用eval方法的执行时间远远慢于其他两种。
152
§12.4 Hook和回调方法
§12.4.1 什么是Hook
最早是在操作系统中出现的Hook概念,在Unix/Linux/Windows中Hook的概念类似,
Hook的目的在于允许用户在系统执行原有流程的过程中,插入自己的代码处理一些额外
的事情。典型的Hook就是使用实现自己功能的方法替换原有的方法,在所需要的额外处理
完成之后,又恢复原有方法的处理流程。
(todo
Hook的图)
§12.4.2 Ruby中的Hook
钩子是一种可以跟踪Ruby事件的技术,例如设定一个钩子侦测对象创建,设定好后,
钩子在发生对象创建时触发。Ruby是动态语言,可以在运行时改变类的方法和属性,我们
可以很容易实现这样的功能。
下面我们来看一个简单的例子,
module Mod
alias_method :old_exit, :exit
def exit(code=0)
puts "Exiting with #{code}"
old_exit(code)
end
end
include Mod
153
exit(99)
执行结果为:
Exiting with code 99
上面的例子中,先使用alias_method给方法添加别名old_new,然后重写exit
方法,在exit方法使用方法的别名来调用原有方法来完成原有功能,然后附加额外的信
息。
同样,我们利用这种技术可以跟踪对象的创建。
class Class
alias_method :old_new, :new
def new(*args)
result = old_new(*args)
puts "The object is: #{result}"
result
end
end
class Test
def initialize(name)
@name = name
end
def to_s
" #{@name}"
end
end
obj1 = Test.new("obj1")
obj2 = Test.new("obj2")
obj3 = String.new("12345")
执行结果为:
The object is: obj1
The object is: obj2
The object is: 12345
154
§11.4.2 回调方法
Ruby实现了一些方法,当特定的事件发生时,相应的方法会被调用。你可以重新定义
这些方法加入额外的处理或者改变系统的默认行为。
Module#method_added
Module#method_removed
Module#method_undefined
Kernel#singleton_method_added
Kernel#singleton_method_removed
Kernel#singleton_method_ undefined
Class#inherited
Module#extend_object
默认情况下,这些方法什么也不做。
§12.5 跟踪程序的运行
§12.5.1 set_trace_func
可以使用Kernel#set_trace_func来跟踪程序的运行。set_trace_func的原型
为:
set_trace_func(proc) => proc
set_trace_func(nil) => nil
创建一个Proc作为跟踪的处理器,参数为nil的时候关闭处理。Proc有六个参数,
一个事件名称,一个文件名,一个行号,一个对象ID,一个binding(todo
?),一个
类名。只要发生事件,Proc就会被调用。事件有这么几种:
155
ccall
调用C语言过程
creturn
从C语言过程中返回
call 调用一个Ruby方法
class 开始一个类或模块的定义
end 结束一个类或模块的定义
line 执行新的一行代码
raise 发起一个异常
return 从Ruby方法中返回
class Test
def test
a = 1
end
end
set_trace_func proc {|event, file, line, id, binding,
classname|
printf "%10s %10s:%2d %10s %8s\n", event, file, line, id,
classname
}
t = Test.new
t.test
执行结果为:
line test.rb:11 false
ccall
test.r b:11 new Class
ccall
test.rb:11 initialize Object
creturn
test.rb:11 initialize Object
creturn
test.rb:11 new Class
line test.rb:12 false
call test.rb: 2 test Test
line test.rb: 3 test Test
return test.rb: 3 test Test
156
§12.5.2 trace_var
可以使用Kernel#trace_var方法对全局变量添加钩子,当对一个全局变量执行赋值操
作时,这个钩子会被调用。第一个参数是一个符号,用来指明要监视哪一个全局变量,第
二个参数是发生赋值操作时执行的Proc对象或块。
trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
$_ = "hello"
$_ = "there"
执行结果为:
$_ is now 'hello'
$_ is now 'there'
§12.5.3 caller
可以使用Kernel#caller来得到当前的调用堆栈。
def meth1
puts caller.join("\n")
end
def meth2
meth1
end
def meth3
meth2
end
meth3
执行结果为:
test.rb:6:in `meth2'
test.rb:10:in `meth3'
157
test.rb:13
§12.5.3 __FILE__,__LINE__和SCRIPT_LINES__
__FILE__ 指明当前的原文件名
__LINE__ 当前在源文件中的行号
__FILE__经常和全局变量$0一起使用来判断一个文件是否由用户直接调用。举个例
子,库文件的开发者经常在所开发的库中包含一些测试代码,这些测试代码在库被其他文
件引用时不会执行。
# library code
# ...
if __FILE__ == $0
# tests...
end
SCRIPT_LINES__默认情况下不会被定义。若将它定义为哈希表后,在解释源代码时,
它将依次处理使用require或load载入的文件,将源文件名作为哈希表元素的索引,
源文件的内容会被按行切分转化为数组作为哈希表的内容。
SCRIPT_LINES__ = {}
require 'Benchmark'
puts "Files: #{SCRIPT_LINES__.keys.join(', ')}"
SCRIPT_LINES__['d:/ruby/lib/ruby/1.8/Benchmark.rb'].each{ |
line|
puts "Source: #{line}"
}
以上代码将输出Benchmark.rb的内容。
158
第十三章 序列化和YAML
§13.1 序列化的概念
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,
它将数据流转换为对象。这两个过程结合起来,就使得数据能够被轻松地存储和传输。
为什么需使用序列化?有两个重要的原因:一个原因是将对象的状态保存在存储介质
中,以便在以后重新创建精确的副本;另一个原因是可以将对象从一个应用程序发送到另
一个应用程序中,远程处理还可以使用序列化将对象从一台机器上的应用程序传递到另一
台机器上的应用程序中。
一般程序在运行时产生对象,这些对象随着程序的停止运行而消失,我们可以通过序
列化将对象保存下来,这样在程序终止运行后这些对象仍然存在。以后可以在程序再次运
行时通过读取数据重建这些对象,也可以在在其他程序中利用这些保存下来的对象。这种
情况下我们通过使用对象的序列化和反序列化来完成这样的功能。
§13.2 使用序列化
§13.2.1 二进制数据保存
在Ruby中,序列化也称为marshaling,使用Marshal类R可以把一个对象转换成字节
159
流,并把它存在应用程序外。这样保存的对象可以在以后被其它的实例或者其它的程序读
取使用。
可以使用Marshal类的dump方法保存对象,以后使用load可以读取以保存的这个对
象。
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def to_s
" #@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = Marshal.dump(obj)
obj = Marshal.load(data)
puts "After: obj = #{obj}"
puts obj.inspect
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 200
#<Rectangle:0x2ac7928 @length=10, @area=200, @width=20>
§13.2.2 YAML数据保存
我们也可以使用YAML来实现序列化:
require 'yaml'
class Rectangle
def initialize(length, width)
160
@length= length
@width = width
@area = length * width
end
def to_s
" #@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = YAML.dump(obj)
obj = YAML.load(data)
puts "After: obj = #{obj}"
puts obj.inspect
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 200
#<Rectangle:0x2ebbe08 @length=10, @area=200, @width=20>
§13.3 定制序列化
§13.3.1 二进制数据保存
并不是所有的数据都可以被序列化和适合序列化,有时候你想自己决定哪些东西被序
列化,Marshal 类提供了二个方法来实现这样的功能,一个是marshal_dump,一个是
marshal_load。当使用Marshal.load对一个对象进行序列化的时候,如果这个对象定义了
marshal_dump或marshal_load方法,那么这个方法将会被调用。
class Rectangle
def initialize(length, width)
@length= length
@width = width
161
@area = length * width
end
def marshal_dump
[ @length, @width ]
end
def marshal_load(variables)
@length = variables[0]
@width = variables[1]
@area = "unknown"
end
def to_s
" #@length #@width #@area"
end
end
obj = Rectangle.new(10, 20)
puts "Before: obj = #{obj}"
data = Marshal.dump(obj)
obj = Marshal.load(data)
puts "After: obj = #{obj}"
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20 unknown
一些对象不能被保存,如果将要被保存的对象包括其它程序调用,方法体,IO实例
等,或者单独的一个对象,或者当你保存一个匿名的类或者方法时,一个TypeError错误
就会产生。
162
§13.3.2 YAML数据保存
使用Marshal模块实现序列化将对象保存为二进制数据,这样的做法有一个显著的缺
点,由于保存的数据是二进制形式,如果解释器发生了重大改变且Marshal的二进制数据
流处理格式和方式也发生了变化,那么就不保证原有已经dump的文件还可以继续使用。
如果使用文本格式的文件来保存序列化数据,那么就不用担心文件是否可读,在
Ruby1.8种,可以使用YAML来实现这样的功能。
对于marshaling,除了二进制格式,也可以采用YAML的方法,它是一种纯文字又易
懂的格式,很有趣而且比起XML简单多了。
修改一下上一节的例子,我们需要定义to_yaml_properties方法,
require 'yaml'
class Rectangle
def initialize(length, width)
@length= length
@width = width
@area = length * width
end
def to_yaml_properties
%w{ @length @width }
end
def to_s
" #@length #@width #@area"
end
end
obj = Rectangle.new(10,20)
puts "Before: obj = #{obj}"
data = YAML.dump(obj)
163
obj = YAML.load(data)
puts "After: obj = #{obj}"
执行结果为:
Before: obj = 10 20 200
After: obj = 10 20
我们可以看看YAML究竟把数据保存成什么样的格式,
obj = Rectangle.new(10,20)
puts YAML.dump(obj)
执行结果为:
!
ruby/object:Rectangle
length: 10
width: 20
§13.3 YAML
看了上一节,大家或许会问,“为什么要命名为 YAML?”已经有许多工具采用了
招人喜欢的“YA*”形式的首字母缩略词,来表示“还有另一种 XXX(Yet Another
XXX)”。在开放源码这个充满智慧的领域中,YAML 没有使用其名称所暗示的首字母缩
略词,而是采用了循环的“YAML 不是标记语言(YAML Ain't Markup Language)”的缩
写。然而对此产生的某种感觉是:YAML 确实可以做标记语言所做的工作,却不需要任何
标记。
尽管YAML与XML一样普通,但在阅读、编辑、修改和产生方面,它比 XML 简单得
多。可以用XML表示的任何东西,都可以用YAML表示,而且几乎总是更紧凑。
XML 是一个典型的由委员会驱动的庞然大物,它试图成为一种文档格式、数据格式、
164
消息包格式、安全的 RPC 通道(SOAP)以及一种对象数据库。而且,XML 为每一类型的
访问和操作都提供了大量的 API:DOM、SAX、XSLT、XPATH、JDOM 以及许多不太常见的
接口层。非常了不起的是 XML 完成了所有这些工作;令人失望的是没有一项工作是完美
无缺的。
YAML的关注面则比较窄,它只是清晰地表示在动态编程语言(如 Perl、Python、Ruby
等)中所遇到的数据结构以及数据类型。
YAML的优点:
 适合人们阅读
 适合与脚本语言交流
 使用宿主语言的内部数据结构
 拥有一致性的信息模型
 使基于流式数据的处理成为可能
 富于表达和可扩展
 容易实现
YAML做为一个可移植的对象序列化方案,可以在不同的Ruby进程中将对象以普通
文本传递,此外也适合ruby与那些支持YAML的语言之间交换数据用。
YAML中数据主要由序列(sequence),map(有的也叫做hash)和标量(scalar)来表
示。语法比较简单,易于人们阅读。
 注释由#开头
 序列由""
开头
 map用key:value的格式
165
 ""
表示一个yaml文档的开始
 list和hash可以嵌套
 block的概念:一个 block 是一段文本。
 Inline Collections:数据都写在一行
§13.3.1 集合类型
§13.3.1.1 序列
 基本序列
可以在每一个新行前加“”
构成一个序列。
YAML中,
Henry
Mike
Tom
在Ruby中,
[‘Henry’, ‘Mike’, ‘Tom’]
 嵌套序列
使用位于空行上的“”,
你可以在一个序列内包含另一个序列。
Henry
Mike
Tom
在Ruby中,
[[‘Henry’, ‘Mike’, ‘Tom’]]
166
使用缩进表示不同的层次,序列也可以表示更深层次的嵌套
Henry
Mike
Tom
在Ruby中,
[[[‘Henry’, ‘Mike’, ‘Tom’]]]
 混合嵌套序列
在序列中也可以嵌套其他的YAML数据结构:
Henry
13
male
Mike
Henry
在Ruby中,
[‘Henry’, [‘13’, ‘male’], ‘Mike’, ‘Henry’]
§13.3.1.2 表
你可以使用Key:value的形式组织数据,每个key:value位于单独的一行,这样的
数据结构称为表,也叫做哈希表或字典。
 基本表
Grant:student
Tom:teacher
在Ruby中,
{‘Grant’ => ‘student’, ‘Tom’ => ‘teacher’ }
 含有序列的表
Grant:student
Tom:
167
teacher
male
在Ruby中,
{‘Grant’ => ‘student’, ‘Tom’ => [‘teacher’, ‘male’ }
 嵌套表
teacher: Mike
students:
Grant:13
Henry:14
在Ruby中,
{ ‘teacher’ => ‘Mike’,
‘students’ => {
‘Grant’ => ‘13’,
‘Henry’ => ‘14’
}
}
 混合表
grade: 1
teachers:
Mike:math
Jane:english
classes:
Grant:
13
Henry:14
Jack:
13
Rose:13
在Ruby中,
{ ‘grade’ => ‘1’,
‘teachers’ =>
168
{
‘Mike’ => ‘math’,
‘Jane’ => ‘English’
}
‘classes’ => [
{
‘Grant’ => ‘13’,
‘Henry’ => ‘14’
},
{
‘Jack’ => ‘13’,
‘Rose’ => ‘13’
}
]
}
 序列中嵌套表的缩写
当在序列中添加一个表时,可以
worked
on test.py:
This
is a test
在Ruby中,
[ {‘worked on test.py’ => [‘This is a test’] } ]
 表中嵌套序列的缩写
当在表中嵌套一个序列时,可以不需要缩进。
Students:

Grant’

Henry’
在Ruby中,
{‘Students’ => [‘Grant’, ‘Henry’] }
 插入键(Merge key)
可以使用插入键在一个表中插入另一个表
169
{ information:
Name: Jane
course: English
<<:
Age: 25
}
在Ruby中,
{‘information’ => {
‘name’ => ‘Jane’,
‘course’ => ‘English’,
‘age’ => ‘25’
}
}
§13.3.2 单行集合类型
 单行序列
 单行映射
§13.3.3 基本类型
 String
 Scalar
 NULL
 Boolean
 Integer
 Float
 Time
 Date
170
§13.3.4 块
§13.3.5 别名和锚(Aliases and Anchors)
§13.3.6 文档
§13.3.7 Ruby中YAML的使用
Ruby中的YAML支持
Ruby1.8已经包含了YAML支持了。只需要require进来就行了。
require 'yaml'
class Person
attr_accessor :name, :sex, :age
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end
#创建一个对象
person=Person.new("Tom", "male", 15)
#打印序列化之后的结果
puts person.to_yaml
171
结果为:
!
ruby/object:Person
age: 15
gender: male
name: Tom
假设现在有一个person.xml文件内容为:
!
ruby/object:Person
age: 25
gender: male
name: Henry
如下代码:
#从person.yml创建对象
person2 = YAML::load( File.open('d:\\person.yml') )
puts person2.inspect
puts person2.class # 结果是Person
puts person2.name
执行结果为:
#<Person:0x2ebbf70 @name="Henry", @age=25, @gender="male">
Person
Henry
第十四章 安全控制
Ruby代码可以方便的移植,也可以方便的对原代码进行分发。这样带来了灵活性的同
时也带来了巨大的隐患,如何才能防止不安全代码的执行呢?
172
我们可以利用Ruby语言含有的安全控制功能来实现这一目标。Ruby安全控制可以用
来锁住有嫌疑的数据,这样的数据被称作“tainted”。Ruby安全控制系统引入了一种机制,
可以由用户决定如何处理有潜在危险的数据。
全局变量$SAFE用来控制安全级别。默认的级别是0。可以通过改变$SAFE的值或使用
命令行参数T
来指定安全级别。安全级别数值越低表示数据越可信。数值低的安全级别的约
束同样对数值高的安全级别起作用。不能在代码中降低当前安全级别。
$SAFE 约束
0 不对tainted数据作检验,是Ruby的默认安全级别
1 不允许有危险的操作使用tainted数据
2 不允许加载可写路径之外的代码
3 所有新建立的对象都被认为是tainted数据
4 将tainted数据和非tainted数据隔离开,非tainted数据不允许改变
新建立的线程会继承当前$SAFE的值。在新建立的线程中,可以改变$SAFE的值而并
不影响其它线程。这样可以容易的实现“沙箱”的功能。例如在一个线程中,当需要从外部
加载代码并加以执行时,可以通过降低$SAFE的值来限制代码执行一些有危险的操作,从
而降低代码的潜在风险。
什么是tainted数据?简单的说,就是这个数据受到了污染而变得不可信。当一个Ruby
对象起源自一些外部资源,例如一个字符串保存了被读取外部文件的内容,这个对象被自
动标记为“tainted”。如果使用这个tainted对象而得到其他的对象,那么这个新得到的对象
也被标记为“tainted”。可以使用Object#tainted?方法来判断一个对象是否被“tainted”。
str1 = "This is a string"
str1.tainted? #=> false
str2 = str1 + “!”
str2.tainted? #=> false
173
str3 = gets #str1的值从输入流读取
str3.tainted? #=> true
str4 = str3 + “!”
str4.tainted? #=> true
可以看到,由于str3的内容来自外部,所谓str3被认为是不可信的,对str3作
操作的结果同样被认为不可信的。
§14.1 0级
0级是默认的安全级别,不对tainted数据作任何检查。环境变量PATH的值会被检查,
只有PATH内包含的目录对所有人都可写PATH才会被置为tainted。 其他任何来自IO的数
据,环境变量和ARGV自动被标记为tainted。
§14.1 1级
对tainted数据有潜在危险的操作被禁止。这一级别适合用来处理输入包含一些不可信
的数据,例如CGI程序。
§14.2 2级
除了1级的约束之外,对文件有潜在危险的操作被禁止。
174
§14.3 3级
除了2级的约束之外,所有新产生的对象都被标记为tainted。
§14.4 4级
除了3级的约束之外,不能改变全局变量的值。
第十五章 单元测试
§15.1 什么是单元测试
§15.2 Ruby单元测试框架
第二部分 内置类与模块
这一部分我们将列出Ruby语言标准内置的类与模块,这些类与模块可以在任何Ruby
175
程序中使用而不需要额外使用require语句。
第一章 内置类
§1.1 Array
Array类是有序的对象集合。Array类以整数作为索引,索引从0开始,负数索引表示
从尾部逆序开始。例如1
表示最后一个元素,2
表示倒数第二个元素,依次类推。
 父类
Array类的父类为Object类。
 Mixin
Enumerable
 类方法
176
§1.2 Bignum
§1.3 Binding
§1.4 Class
§1.5 Continuation
Continuation 对象由Kernel#callcc 方法产生,功能有些类似C 语言中的
setjmp/longjmp,它保存了执行环境,可以供代码以后使用。
Continuation对象只有一个call方法,当执行了call方法后,当前执行语句会
转到Continuation对象的块后面,继续执行下面的代码。
obj = callcc{|continuation| continuation}
puts "start..."
obj.call if obj
puts "end..."
执行结果为:
start...
start...
end...
上面的例子中,首先使用callcc 生成一个Continuation 对象,然后输出
“start…”在调用call 后,当前执行语句跳转到callcc 语句紧接的块后,所以
177
“start…”被输出了两次。在执行了一次call方法后,obj对象会被置为nil。
下面一个例子是在方法内发生跳转:
def method
callcc {|cont| return cont}
puts "in method..."
end
puts "start..."
obj = method()
puts "end..."
obj.call if obj
执行结果为:
start...
end...
in method...
end...
178
§1.6 Dir
§1.7 Exception
§1.8 FalseClass
§1.9 File
§1.10 File::Stat
§1.11 Fixnum
§1.12 Float
§1.13 Hash
§1.14 Integer
179
§1.15 IO
§1.16 MatchData
§1.17 Method
§1.18 Module
§1.19 NilClass
§1.20 Numeric
§1.21 Object
§1.22 Proc
§1.23 Process::Status
180
§1.24 Range
§1.25 Regexp
§1.26 String
§1.27 Struct
§1.28 Struct::Tms
§1.29 Symbol
§1.30 Thread
§1.31 ThreadGroup
§1.32 Time
181
§1.33 TrueClass
§1.34 UnboundMethod
182
第二章 内置模块
§2.1 Comparable
§2.2 Enumerable
§2.3 Error
§2.4 FileTest
§2.5 GC
§2.6 Kernel
§2.7 Marshal
§2.8 Math
183
§2.9 ObjectSpace
§2.10 Process
§2.11 Process::GID
§2.12 Process::Sys
§2.13 Process::UID
§2.14 Signal
all?方法
方法原型:
enum.all? [ {|obj| block} ]
方法依次将集合内的每个元素传递给块,如果所有的执行结果都为真那么方法返回
true,任意一次执行结果为假那么方法返回false。如果忽略了block块,则Ruby会自动
添加一个{|obj|obj}块。
%w{ ant bear cat}.all? {|word| word.length >= 3} #=> true
184
%w{ ant bear cat}.all? {|word| word.length >= 4} #=> false
[ nil, true, 99 ].all? #=>
false
Inject方法
方法原型:
enum.inject(initial) {| memo, obj | block } => obj
enum.inject {| memo, obj | block } => obj
memo被设置为块计算后的返回值。首先,用initial初始化memo,并将memo与
obj参数传递给块进行计算,然后将上步块计算后的值赋值给memo后,再继续计算块,
以此类推。
若省略了初始值initial,开始时会把第一和第二个元素传递给块。若只有一个元素时,
将直接返回首元素,而不会执行块。 若没有元素,则返回nil。
# 求5到10的和
(5..10).inject {|sum, n| sum + n } #=> 45
# 求5到10的积
(5..10).inject(1) {|product, n| product * n } #=> 151200
# 找出最长的单词
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
# 找出最长单词的长度
longest = %w{ cat sheep bear }.inject(0) do |memo,word|
memo >= word.length ? memo : word.length
end
longest #=> 5
185
这个方法之所以用inject这个名称,是因为这个方法有注射动作,即将initial及后
来的块计算结果注入到memo中。
第三部分 Ruby语言总结
186
 书写习惯
Ruby是一种面向行的语言,一行可以写多个语句,使用“;”隔开。一条语句也
可以写在多行,行之间使用“\”连接。
 语言注释
Rub中提供两种形式的注释:
1. 单行注释,以“#”开始直到行末尾。
2. 多行注释,在“=begin”和“=end”之间的行会被解释器忽略。
 BEGIN和END块
每一个Ruby 源文件可以声明BEGIN 块和END 块,BEGIN 块在文件载入被执行,
END块在程序执行完毕被执行。
BEGIN {
#Begin code
}
END {
#End code
}
 常用分隔符输入
分隔符输入格式以一个“%”开始,紧接着一个字符表明输入类型,随后的字符表示
分隔符。分隔符可以是任何的非字母单字节字符。如果分隔符是“(,[,{,<”,则表
示结束的分隔符为相应的“),],},>”,而且可以嵌套出现。其它的分隔符则是分隔
字符下一次出现的位置。
%Q/This is a string/
%Q<This <is> a string>
187
使用分隔符也可以跨越多行:
string = %q{
BEGIN {
puts "===BEGIN==="
}
}
%q 单引号包围的字符串
% 双引号包围的字符串
%Q 双引号包围的字符串
%w 每一个元素都是字符串的数组
%W 每一个元素都是字符串的数组
%r 正则表达式
%x Shell命令
 命名规则
Ruby中的对象和常量保存了对实际对象的引用,对象自己并没有类型,所谓的
对象的类型是指它引用的实际对象的类型。
常量可以在类或模块中定义,访问时需要使用类名加上“::”。
 变量
在Ruby中使用不同的前缀来区分变量的不同类型:
类型示例解释
全局变量$foo 全局变量以$开始,未初始化的全局变量值为nil
实例变量@foo 实例变量以@开始,未初始化的实例变量值为nil
类变量@@foo 类变量以@@开始,必须初始化
局部变量foo 局部变量以小写字母或下划线开始
常量Foo 常量以大写字母开始
 预定义变量
 异常相关
名称类型解释
188
$! Exception 最近被抛出的异常对象,可以在rescue语句中
使用=>访问
$@ Array 最近被抛出异常相关的堆栈信息,可以使用
Exception#backtrace方法访问
 正则表达式匹配相关
名称类型解释
$& String 已经匹配到的字符串
$@ String 最后一个使用括号匹配到的字符串
$` String 前一个匹配到的字符串
$’ String 下一个匹配到的字符串
$= Object (已废弃)如果内容不为nil或failse,那么正则
表达式匹配,字符串比较和Hash表的值不区分
大小写
$1到$9 String 正则表达式匹配使用圆括号相应的匹配串
$~ MatchData 保存正则表达式匹配结果
 输入输出相关
名称类型解释
$/ String 输入分隔符
$0
String 和$/同义
$\ String 输出分隔符
$, String 使用Array#join方法输出时的分隔符,默认为nil
$. Fixnum
$; String 使用String#split方法时的分隔符,默认为nil
$< Object ARGF缩写
$> IO $defout缩写
$_ String
$defout IO
$deferr IO
$F
String
$stderr IO
$stdin IO
$stdout IO
 环境相关
名称类型解释
$0 String 当前执行应用程序名
$* Array 保存了命令行执行的参数
189
$” Array
$$ Fixnum
$? Process::status
$: Array
$a
Object
$d
Object
$DEBUG Object
__FILE__ String
$F Array
$FILENAME String
$i
String
$I
Array
$K
String
$l
Object
__LINE__ String
$LOAD_PATH Array
$p
Object
$SAFE Fixnum
$VERBOSE Object
$v
Object
$w
Object
 标准对象
名称类型解释
ARGF Object 和$<同义
ARGV Array 和$*同义
ENV Object
false FalseClass
nil NilClass
self Object
true TrueClass
 全局常量
名称类型解释
DATA IO 和$<同义
FALSE FalseClass 和$*同义
NIL NilClass
RUBY_PLATFORM String
RUBY_RELEASE_DATE String
RUBY_VERSION String
STDERR IO
190
STDIN IO
STDOUT IO
SCRIPT_LINES__ Hash
TOPLEVEL_BINDING Binding
TRUE TrueClass
附录
§1 术语对照
Singleton Method 单例方法
Singleton Class 单件类
iterator 迭代器
handler 处理器
callback 回调
class Integer
def get_debug_info
"This is #{self}"
end
end
puts (0..5).map { |i| i.get_debug_info }
执行结果为:
191
This is 0
This is 1
This is 2
This is 3
This is 4
This is 5
192
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值