Docstoc

Ruby程序设计

Document Sample
Ruby程序设计 Powered By Docstoc
					                                                   Ruby 程序设计

                                                      目               录
第一部分 Ruby 语言基础 ...............................................................................................................6
第一章 Ruby 语言概述 .....................................................................................................................6
  §1.1 Ruby 的历史 ....................................................................................................................6
  §1.2 Ruby 名字的由来 ............................................................................................................6
  §1.3 Ruby 的特点 ....................................................................................................................7
  §1.4 Ruby 和 Python 的比较 ...................................................................................................7
第二章 Ruby 编程环境 .....................................................................................................................8
  §2.1 Ruby 的安装 ..................................................................................................................8
      §2.1.1 在 Windows 95/98/Me/XP 上安装 Ruby ...........................................................8
      §2.1.2 在 Linux 上安装 Ruby .......................................................................................8
  §2.2 运行 Ruby ......................................................................................................................9
      §2.2.1 使用 Ruby ...........................................................................................................9
      §2.2.2 使用 FreeRIDE 和 SciTE .................................................................................10
      §2.2.3 使用 fxri ............................................................................................................12
  §2.3 Ruby-irb .......................................................................................................................13
  §2.4 Ruby-ri .........................................................................................................................14
  §2.5 RubyGems....................................................................................................................15
第三章 类与对象...........................................................................................................................16
  §3.1 类的定义........................................................................................................................16
  §3.2 对象,属性和方法........................................................................................................17
  §3.3 继承 ...............................................................................................................................21
  §3.4 特殊方法与特殊类........................................................................................................22
  §3.5 类变量与类方法............................................................................................................24
  §3.4 存取控制........................................................................................................................25
  §3.6 元类 ...............................................................................................................................27
  §3.7 Ruby 的动态性 ..............................................................................................................28
  §3.8 变量 ...............................................................................................................................29
      §3.8.1 局部变量.............................................................................................................29
      §3.8.2 实例变量.............................................................................................................30
      §3.8.3 类变量.................................................................................................................31
      §3.8.4 全局变量.............................................................................................................31
      §3.8.5 常量.....................................................................................................................32
  §3.8 与定义有关的操作........................................................................................................33
      §3.8.1 alias .....................................................................................................................33
      §3.8.2 undef ...................................................................................................................34
      §3.8.3 defined? ...............................................................................................................35
第四章 基本类型.............................................................................................................................38
  §4.1 Array ..............................................................................................................................38
  §4.2 Hash ...............................................................................................................................39
                                                                                                                                            2


  §4.3 Number ..........................................................................................................................40
  §4.4 String ..............................................................................................................................41
  §4.5 Range .............................................................................................................................42
  §4.6 Symbol ...........................................................................................................................43
  §4.7 正则表达式....................................................................................................................44
第五章 代码块和迭代器.................................................................................................................46
  §5.1 代码块(Block) ...............................................................................................................46
      §5.1.1 什么是代码块.......................................................................................................46
      §5.1.2 代码块与对象.......................................................................................................46
  §5.2 迭代器(Iterator) .............................................................................................................48
      §5.2.1 什么是迭代器.......................................................................................................48
      §5.2.2 使用迭代器...........................................................................................................48
      §5.2.3 yield........................................................................................................................49
      §5.2.4 编写自己的迭代器...............................................................................................50
第六章 表达式 ................................................................................................................................51
  §6.1 运算符............................................................................................................................52
  §6.2 命令替换........................................................................................................................53
  §6.3 赋值运算符....................................................................................................................54
  §6.4 并行赋值........................................................................................................................55
  §6.5 嵌套赋值........................................................................................................................56
  §6.6 其他赋值........................................................................................................................56
  §6.7 条件运算........................................................................................................................56
  §6.8 case 表达式....................................................................................................................58
  §6.9 循环 ...............................................................................................................................60
      §6.9.1 Loop .......................................................................................................................60
      §6.9.2 While ......................................................................................................................60
      §6.9.3 Until .......................................................................................................................60
      §6.9.4 Iterator ....................................................................................................................60
      §6.9.5 For..In .....................................................................................................................61
      §6.9.6 Break,Redo,Next ..............................................................................................62
           §6.9.6.1 break ............................................................................................................62
           §6.9.6.2 redo .............................................................................................................63
           §6.9.6.3 next ..............................................................................................................64
      §6.9.7 Retry.......................................................................................................................65
第七章 方法 ....................................................................................................................................65
      §7.1 运算符重定义..........................................................................................................68
      §7.2 变长参数..................................................................................................................68
      §7.3 块调用......................................................................................................................69
      §7.4 方法返回值..............................................................................................................70
第八章 模块 ....................................................................................................................................72
  §8.1 名字空间........................................................................................................................72
  §8.2 mixin ..............................................................................................................................73
  §8.3 使用 mixin .....................................................................................................................74
      §8.3.1 Comparable .........................................................................................................74
                                                                                                                                             3


       §8.3.2 Enumerable .........................................................................................................75
       §8.3.3 Singleton .............................................................................................................76
  §8.4 Require, load 和 include ................................................................................................77
第九章 异常 ..................................................................................................................................78
  §9.1 异常处理..........................................................................................................................79
  §9.2 定义异常类......................................................................................................................84
  §9.3 catch 和 throw ...................................................................................................................84
第十章 多任务处理.......................................................................................................................85
  §10.1 多线程处理..................................................................................................................85
       §10.1.1 线程创建...........................................................................................................86
       §10.1.2 线程操作...........................................................................................................87
       §10.1.3 线程和异常.......................................................................................................88
       §10.1.4 线程调度...........................................................................................................91
       §10.1.5 线程同步...........................................................................................................92
            §10.1.5.2 Monitor...................................................................................................93
            §10.1.5.2 Mutex .....................................................................................................96
            §10.1.5.3 Queue .....................................................................................................98
  §10.2 多进程处理..................................................................................................................99
       §10.2.1 进程创建...........................................................................................................99
            §10.2.1.1 system 方法和反引号............................................................................99
            §10.2.1.2 popen 方法 ........................................................................................... 100
            §10.2.1.3 fork 方法 .............................................................................................. 100
第十一章        基本 I/O 操作 ......................................................................................................... 101
  §11.1 使用 Kernel 模块处理 I/O 操作.................................................................................. 101
  §11.2 文件处理....................................................................................................................... 101
  §11.3 StringIO ......................................................................................................................... 102
  §11.4 Socket ............................................................................................................................ 103
第十二章 反射和对象空间........................................................................................................... 104
  §12.1 ObjectSpace 模块 ...................................................................................................... 104
  §12.2 察看类和对象的状态 ................................................................................................ 106
  §12.3 动态方法调用............................................................................................................ 108
       §12.3.1 使用 send 方法 ............................................................................................... 108
       §12.3.2 使用 Method 类和 UnboundMethod 类 ......................................................... 109
       §12.3.3 使用 eval 方法 ................................................................................................ 111
       §12.3.4 性能................................................................................................................. 111
  §12.4 Hook 和回调方法 ...................................................................................................... 112
       §12.4.1 什么是 Hook ................................................................................................... 112
       §12.4.2 Ruby 中的 Hook ............................................................................................. 112
       §11.4.2 回调方法 ......................................................................................................... 114
  §12.5 跟踪程序的运行........................................................................................................ 114
       §12.5.1 set_trace_func ....................................................................................... 114
       §12.5.2 trace_var .......................................................................................................... 115
       §12.5.3 caller ................................................................................................................ 115
       §12.5.3 __FILE__,__LINE__和 SCRIPT_LINES__ .................................................. 116
                                                                                                                                                4


第十三章 序列化和 YAML .......................................................................................................... 117
  §13.1 序列化的概念............................................................................................................ 117
  §13.2 使用序列化................................................................................................................ 117
       §13.2.1 二进制数据保存............................................................................................. 117
       §13.2.2 YAML 数据保存 ............................................................................................ 118
  §13.3 定制序列化................................................................................................................ 119
       §13.3.1 二进制数据保存............................................................................................. 119
       §13.3.2 YAML 数据保存 ............................................................................................ 120
  §13.3 YAML ........................................................................................................................ 121
       §13.3.1 集合类型......................................................................................................... 122
             §13.3.1.1 序列...................................................................................................... 122
             §13.3.1.2 表.......................................................................................................... 123
       §13.3.2 单行集合类型................................................................................................. 125
       §13.3.3 基本类型......................................................................................................... 125
       §13.3.4 块..................................................................................................................... 126
       §13.3.5 别名和锚(Aliases and Anchors) ................................................................ 126
       §13.3.6 文档................................................................................................................. 126
       §13.3.7 Ruby 中 YAML 的使用 .................................................................................. 126
第十四章 安全控制....................................................................................................................... 127
  §14.1 0 级 ............................................................................................................................ 128
  §14.1 1 级 ............................................................................................................................ 128
  §14.2 2 级 ............................................................................................................................ 128
  §14.3 3 级 ............................................................................................................................ 128
  §14.4 4 级 ............................................................................................................................ 128
第十五章 单元测试....................................................................................................................... 129
  §15.1 什么是单元测试........................................................................................................ 129
  §15.2 Ruby 单元测试框架 .................................................................................................. 129
第二部分 内置类与模块............................................................................................................. 129
第一章 内置类 .............................................................................................................................. 129
  §1.1 Array ............................................................................................................................... 129
  §1.2 Bignum............................................................................................................................ 130
  §1.3 Binding............................................................................................................................ 130
  §1.4 Class ................................................................................................................................ 130
  §1.5 Continuation.................................................................................................................... 130
  §1.6 Dir ................................................................................................................................... 132
  §1.7 Exception ........................................................................................................................ 132
  §1.8 FalseClass ....................................................................................................................... 132
  §1.9 File .................................................................................................................................. 132
  §1.10 File::Stat ........................................................................................................................ 132
  §1.11 Fixnum .......................................................................................................................... 132
  §1.12 Float .............................................................................................................................. 132
  §1.13 Hash .............................................................................................................................. 132
  §1.14 Integer ........................................................................................................................... 132
  §1.15 IO .................................................................................................................................. 132
                                                                                                                                               5


  §1.16 MatchData ..................................................................................................................... 132
  §1.17 Method .......................................................................................................................... 132
  §1.18 Module .......................................................................................................................... 133
  §1.19 NilClass ......................................................................................................................... 133
  §1.20 Numeric......................................................................................................................... 133
  §1.21 Object ............................................................................................................................ 133
  §1.22 Proc ............................................................................................................................... 133
  §1.23 Process::Status .............................................................................................................. 133
  §1.24 Range ............................................................................................................................ 133
  §1.25 Regexp .......................................................................................................................... 133
  §1.26 String ............................................................................................................................. 133
  §1.27 Struct ............................................................................................................................. 133
  §1.28 Struct::Tms .................................................................................................................... 133
  §1.29 Symbol .......................................................................................................................... 133
  §1.30 Thread ........................................................................................................................... 134
  §1.31 ThreadGroup ................................................................................................................. 134
  §1.32 Time .............................................................................................................................. 134
  §1.33 TrueClass....................................................................................................................... 134
  §1.34 UnboundMethod ........................................................................................................... 134
第二章 内置模块........................................................................................................................... 135
  §2.1 Comparable ..................................................................................................................... 135
  §2.2 Enumerable ..................................................................................................................... 135
  §2.3 Error ................................................................................................................................ 135
  §2.4 FileTest ............................................................................................................................ 135
  §2.5 GC ................................................................................................................................... 135
  §2.6 Kernel.............................................................................................................................. 135
  §2.7 Marshal ........................................................................................................................... 135
  §2.8 Math ................................................................................................................................ 135
  §2.9 ObjectSpace .................................................................................................................... 135
  §2.10 Process .......................................................................................................................... 135
  §2.11 Process:    :GID ............................................................................................................. 135
  §2.12 Process:    :Sys............................................................................................................... 136
  §2.13 Process:    :UID ............................................................................................................. 136
  §2.14 Signal ............................................................................................................................ 136
第三部分 Ruby 语言总结 ........................................................................................................... 137
附录     141
  §1 术语对照......................................................................................................................... 141
                                                             6




第一部分            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 并不能完全算作“面向对象”语言。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 的后续语言十分合适。
                                                        7



§1.3 Ruby 的特点

  Ruby 是一种功能强大的面向对象的脚本语言,      可以使用它方便快捷地进行面向对象程序
设计。与 Perl 类似,而且 Ruby 具有强大的文本处理功能,使文本处理变得简单。此外还可
以方便地使用 C 语言来扩展 Ruby 的功能。
  若您曾经“想要一种简单的面向对象的语言”       ,或者认为“Perl 的功能虽然好用,但它的
语法真让人受不了”   ,又或者觉得“LISP 系列语言的思想不错,但到处都是括号真让人讨厌,
最起码算式应该按照通常的样式书写”       。那么,Ruby 或许能让您满意。

归纳起来,Ruby 有以下优点:
 解释型执行,方便快捷
Ruby 是解释型语言,其程序无需编译即可执行。
 语法简单、优雅
语法比较简单,类似 Algol 系语法。
 完全面向对象
Ruby 从一开始就被设计成纯粹的面向对象语言,因此所有东西都是对象,例如整数等基本数
据类型。
 内置正则式引擎,适合文本处理
Ruby 支持功能强大的字符串操作和正则表达式检索功能,可以方便的对字符串进行处理。
 自动垃圾收集
具有垃圾回收(Garbage Collect,GC)功能,能自动回收不再使用的对象。不需要用户对内
存进行管理。
 跨平台和高度可移植性
Ruby 支持多种平台,在 Windows, Unix, Linux, MacOS 上都可以运行。Ruby 程序的
可移植性非常好,绝大多数程序可以不加修改的在各种平台上加以运行。
 有优雅、完善的异常处理机制
Ruby 提供了一整套异常处理机制,可以方便优雅地处理代码处理出错的情况。
  拥有很多高级特性
Ruby 拥有很多高级特性,例如操作符重载、Mix-ins、特殊方法等等,是用这些
特性可以方便地完成各种强大的功能。

同时,由于是解释型语言,Ruby 也有下列缺点:
 解释型语言,所以速度较慢
 静态检查比较少


§1.4 Ruby 和 Python 的比较

                                 。但 matz 认为 Python 的功能仍
  Python 是 Ruby 的劲敌。其功力深厚,可谓“千年蛇妖”
不完美,不然就不会创造 Ruby 了。
                                                                                       8




第二章 Ruby 编程环境

§2.1        Ruby 的安装

    Ruby 支持多种平台,包括 Windows、Linux、各种类 UNIX、MacOS X 等。


§2.1.1         在 Windows 95/98/Me/XP 上安装 Ruby

    对于使用 Windows 平台的用户,安装 Ruby 是相当简单直接的事情。最方便的方法是使
用“One-Click Ruby Installer”    。
    不知你有没有听说过 SourceForge?SourceForge 是全球最大的开放源代码软件开发平台
和仓库。它集成了很多开放源代码应用程序,为软件开发提供了整套生命周期服务。在 Ruby
世界,也有一个类似的网站,那就是 Rubyforge。                      “One-Click Ruby Installer”是 Rubyforge 上
的一个开源项目,           也是 Rubyforge 上下载量最大的项目之一。               这个项目将 Ruby 语言核心和一
系列常用扩展集成到了一起,还包含支持 Ruby 的免费的 IDE 工具 FreeRIDE 和 SciTE,除了
这些之外还包括帮助文档,                 示例代码,      RubyGems 包管理器, GUI 库, (Interactive Ruby
                                                          Fox          fxri
Help & Console)等。和正如它名字所示,使用它,Ruby 安装变得前所未见的容易。你可以
在下面的地址下载到它的最新版本:
    http://rubyforge.org/projects/rubyinstaller/


§2.1.2         在 Linux 上安装 Ruby

  在 linux 下 Ruby 的安装要稍微复杂一些,推荐使用源码编译的方式安装,这样可以保证
安装的是最新版本。
  首先到 ruby 主站http://www.ruby-lang.org/en/ 下载源代码,下载完毕后解压到目录,然
后使用以下命令:
  ./configure
  ./make; make install
     执行上面的命令需要 root 权限,默认安装到/usr/local 下。你也可以使用“./configure
--prefix=自定义路径”来指定安装目录。
     windows 上的 ruby one-click installer 默认安装了 RubyGems,但在 Linux 下我们需要手动
安装 RubyGems。RubyGems 是一个 Ruby 的包管理器,我们后边会讲到它。
     首先从 Rubyforge 下载 RubyGems 的最近版本,地址如下:
     http://rubyforge.org/projects/rubygems/
     解压 RubyGems 以后到相应目录下输入 ruby setup.rb,屏幕上打印一些日志以后会告诉
你安装成功,执行 gem -v 可以查看 gem 安装版本号。
                                                            9



§2.2        运行 Ruby

   下面,我们将以 Windows 平台下的 Ruby 环境举例如何运行 Ruby。


§2.2.1        使用 Ruby


    将“Hello World”作为学习计算机语言第一个学写的程序,现在已经成为一种传统。该
程序最早出现在由 Brian Kernighan 和 Dennis Ritchie 写的经典计算机程序设计教程《The C
                    。我们来看看 Ruby 世界的“Hello World”
Programming Language》                           :

  在 Windows 中,打开命令行提示符窗口,在提示符上输入“Ruby”并回车,Ruby 解释
器就会运行并等候输入程序。Ruby 可执行文件应该包含在系统搜索路径内。
  输入下面的程序:

   print "Hello World!"

   然后按 Ctrl+D 再按回车键,你就会看到 Ruby 执行程序的输出结果:




   你也可以先将代码保存为文件,然后使用再 Ruby 解释器执行:
                                                           10




§2.2.2    使用 FreeRIDE 和 SciTE

    FreeRIDE 是一个支持 Ruby 语言的免费 IDE 环境。FreeRIDE 本身就是使用 Ruby 语言开
发,它也是 Rubyforge 上的重要项目之一。
    可以使用 FreeRIDE 来编写调试和执行 Ruby 代码,FreeRIDE 内置了交互式变成环境和
Ruby 语言在线帮助,功能十分强大。
                                                       11




     Scintilla 是一个免费的源代码编辑控件,它完全开放源代码,并允许用户自由地用于开
源软件或是商业软件中。SciTE 是用这个控件开发了一个编辑软件,在“One-Click Ruby
Installer”中,SciTE 集成了 Ruby 语言支持,使用起来非常方便。相比 FreeRIDE,它的特点
就是使用简单。
                                                              12




§2.2.3    使用 fxri

   Fxri 是一个 Ruby 交互帮助和控制台工具。它不仅可作为语言的在线帮助,而且可以用作
交互式 Ruby 解释器来执行程序。对于学习 Ruby 语言,fxri 是一个非常方便的帮手。
   不知你有没有听说过 Fox ToolKit,它是相当轻巧的开放源代码的图形库。FXRuby 是
RubyForge 上的一个项目,                              而
                 提供了 Ruby 语言使用 Fox ToolKit 的接口。 Fxri 正是基于 FXRuby
开发,Fxri 同样是 RubyForge 上的项目。这样你应该可以猜到 Fxri 名字的由来
                                                              13




  Fxri 同时集成了 Ruby-irb 和 Ruby-ri 的功能,有了它,你可以抛开 Ruby-irb,Ruby-ri 了,
但如果你用的不是 Windows 系统的话,算我没说


§2.3    Ruby-irb

  Ruby-irb 是交互式 Ruby(Interactive Ruby)的简称,用来从标准输入读入并执行 Ruby
代码的工具,像一个 shell。
  使用命令“irb”进入交互式模式,然后可以象输入命令行命令一样输入 Ruby 代码,代
码执行的结果会立刻显示:
                                                       14




§2.4    Ruby-ri

  和 Perl 一样,Ruby 也设计了嵌入式文档。 ruby-ri 就是查看文档的工具。Ruby-ri 的执行
命令为“ri”  ,例如你可以通过“ri String.new”来查询 String 类的 new 方法:
                                                          15



§2.5    RubyGems

  RubyGems 是 Ruby 社区流行的包管理工具,在以前如果要下载一个 Ruby 扩展或者应用
程序的话,你需要先下载相应的 zip 包,然后解压缩,再将应用或者扩展安装到 Ruby 对应的
目录中。但是有了 RubyGems 所有这些麻烦都没有了,你只需要一条命令就可以从远程服务
器上下载相应的包,如果相应的应用包含其他扩展,RubyGems 会提示你从远程安装所依赖
的扩展。安装后 RubyGems 会运行相应的程序生成 rdoc 帮助文档。当然你也可以将软件包
下载到本地运行 RubyGems 本地安装命令。
  统一化的管理带来的好处就是简单,            有了 RubyGems 包管理器,Ruby 应用的安装将变得
前所未见的容易。RubyGems 是 Rubyforge 下载量最大的项目之一,现在 Ruby 社区的应用都
在朝着 RubyGems 的方向发展,RubyGems 也将成为 Ruby 事实上的包管理器标准。
  RubyGems 包管理器的可执行命令是“gem”        ,gem 命令包含很多子命令和相应的选项,
例如:
  gem -h/--help    – 显示命令帮助
  gem -v/--version – 显示 Gems 的版本号
                                                16




第三章 类与对象
Ruby 是一种真正的面向对象程序设计语言,面向对象指以对象为中心的理论体系。
 封装(Encapsulation)
   将内部结构和算法隐藏起来,以确保只有特定的过程(也叫方法)才能直接操作数据,其
结果是不能从外部直接使用数据构造,同时一旦内部构造发生变化也不会对外界造成不良影
响。这种隔离方法就叫做封装。
 继承
 多态(Polymorphism)
   根据对象的不同选择合适的操作。在 Ruby 中的实现方法是,根据被调的对象的不同来
选择不同的方法。

     虽然有很多语言都宣称自己是面向对象的,但是他们往往对面向对象的解释都一样,大
多是以自己特有的方式来解释什么是面向对象,而在实际情况中,这些面向对象语言又采用
了很多非面向对象的做法。
     以 Java 为例:如果你想取一个数字取绝对值,java 的做法是:
     int num = Math.abs(-99);
     也就是将一个数值传递给 Math 类的一个静态函数 abs 处理。为什么这么做?因为在
java 中,数值是基本类型不是类。
     而在 Ruby 中,任何事物都是对象,也就是说,数字–99 就是对象,取绝对值这样的操
作应该属于数字本身,所以 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
                                                   17


      @gender = gender
      @age = age
    end
  end


  若某个类已经被定义过,此时又用相同的类名进行类定义的话,就意味着对原有的类的
定义进行追加。

  class Test
      def meth1
          puts "This is meth1"
      end
  end

  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 方法是一个特殊的方法,
                                                         18


这个方法在构造实例对象时会被自动调用。
   对实例进行初始化操作时,需要重定义 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 中,一个对象的内部属性都是私有的。 上面的代码中,我们定义了方法 name,
gender,age 三个方法用来访问 Person 类实例对象的实例变量。注意 name,gender,age 访问
只能读取相应实例变量,而不能改变它们的值。

  我们也可以用成员变量只读控制符 attr_reader 来达到同样的效果。
  class Person
      attr_reader :name, :gender, :age
  end


  类似地,我们可以定义方法去改变成员变量的值。
  class Person
     def name=(name)
                                                                19


              @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


  也可以使用 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
                                                       20


  执行结果为:
  value
  value=


   上面代码中,我们使用 Test.instance_methods 得到 Test 类所有的实例方法,使
用 Test.superclass.public_methods 得到 Test 父类所有的实例方法,然后相减就得
到 Test 类不包含父类的所有的实例方法。
   由于 instance_methods 方法返回值为一个 Array,所以我们作差值运算,Array
的具体操作后面章节会讲到。

  也可以重定义方法,重定义一个方法时,新的定义会覆盖原有的定义。

  下面的例子重定义类中的方法 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
                                                   21




     def <=>(other)
       self.age <=> other.age
     end
   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")


执行结果为:
  This is derived test
  This is Base test
                                                        22




如果传入的参数被修改再调用 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)。

  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
                                          23


  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
      ……
  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
                                            24


        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




§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
                                              25


     @gender = gender
     @age = age
     @@number += 1
   end


  def Person.getNumber #类方法
      return @@number
    end
  end


  除了 Person.getNumber 这种方式定义类方法外,还可以使用其它方式定义类方法,
在后续章节可以陆续见到。




§3.4 存取控制

  当你设计一个类时,你需要决定哪些属性和方法可以在类外被访问到,哪些属性和方法
在类外被隐藏。如果一个类有过多的属性和方法在类外可以被访问到,那么势必破坏这个类
的封装性。幸运的是在 Ruby 中,只能通过方法去改变一个类的属性,这样我们只需要考虑
方法的存取控制。
方法的存取控制有三种:
 公有方法(Public Method)
   方法在任何地方都可以被调用,这是方法的默认存取控制。除了 initialize 和
    initialize_cpoy 方法,他们永远是私有方法。
 保护方法(Protected Method)
   方法只能被定义这个方法的类自己的对象和这个类的子类的对象所访问。
 私有方法(private Method)
   方法只能被定义这个方法的类的对象自己访问,即使是这个类的其他对象也不能访
    问。

  Ruby 中的保护方法和私有方法与一般面向对象程序设计语言的概念有所区别,保护方
法的意思是方法只能方法只能被定义这个方法的类自己的对象和子类的对象访问,私有方法
只能被对象自己访问。

  class Test
     def method1   #默认为公有方法
     …
     end


     protected     #保护方法
     def method2
     …
     end
                                                   26


        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
  ...
  end

  def method2
  ...
  end

  def method3
  ...
  end

  def methdo4
  ...
  end

  public    :method1
  protected :method2
  private   :method3, :method4
  end
                                                  27




  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


  没错,类也是对象,这是 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
                                   28


       def self.meth2
           # ...
       end


       #定义类方法方式 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


 执行结果为:
 This is meth
 This is new meth
                                                               29


   可以使用 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 变量名的首字符来区分它是局部
变量、实例变量、类变量、全局变量还是常量。通常情况下,变量名的第二位字符以后是数
字、字母或下划线,但有的内部变量名比较特殊,如“$?”  。




§3.8.1 局部变量

      局部变量以小写字母或下划线开始。

      num = 1
      foo


     局部变量的作用域起始于声明处,结束于该声明所在的块、方法定义、类/模块定
   义的结尾。

      2.times {
        p defined?(num)
        num = 10
                                              30


        p num
    }


    输出为:
    nil
    10
    nil
    10


    即使声明部分未被解释器执行仍有效,因为已经经过解释器的处理。

    v = 1 if false
    p defined?(v)
    p v


    输出为:
    "local-variable"
    nil


    但若块已经变成过程对象的话,则局部变量将一直持续到该过程对象终结为止。若
  多个过程对象引用同一个作用域的话,局部变量将被这些对象所共享。
    (to-do 例子)




§3.8.2 实例变量

  以@开始的变量是实例变量,实例变量属于特定的对象。
  class Person
    def initialize(name, gender, age)
      @name = name
      @gender = gender
      @age = age
    end
  end


  上面的例子中,@name, @gender,@age 都是实例变量。可以在类或子类的方法中引
用实例变量。若引用尚未被初始化的实例变量的话,其值为 nil。
                                           31



§3.8.3 类变量

  以@@开始的变量是类变量。类变量在类的定义中定义,可以在类的特殊方法、
实例方法等处对类变量进行赋值和引用。类变量被类,类的子类和他们的实例对
象共享。
  class Person
      @@number = 0 #使用前必须有初值
    def initialize(name, gender, age)
      @name = name
      @gender = gender
      @age = age
      @@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 运行时环境预定义了一系列的全局变量,有关预定义的全局变量的信息,请参见附
表。
                                                                32



§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


  在类定义表达式生成类对象的同时,还会将类对象赋值给一个与该类同名的常数,引用
类名也就是引用该常数。
  class Test
  end

     p Test.class           #Class
     p Test                 #test


     若想访问 Object 类中的常数(顶层的常数)时,也需要也使用"::"操作符,但操作符左边为
空。
                                              33



§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
    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 等添加别名,另外,有些全局变量对于解释器来
说是举足轻重的,若重新定义它们的话,有时会影响解释器的正常工作。
                                                                       34



§3.8.2 undef

  undef 用来取消一个方法的定义,也可以使用 Module#undef_method 方法取消方法的定
义。undef 会取消方法名和方法定义之间的关系,即使超类中有同名方法,调用时也会引发
异常。

   class Base
       def meth
           puts "This is Base#meth"
       end
   end

   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"
                                                        35


        end
  end

  class Test2 < Derived
  def meth
      puts "This is Test2#meth"
  end

  remove_method(:meth)
  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?    $*          #=>   "global-variable"
  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 取消了原有定义,
defined?都将返回 nil。
   注意如果一个方法以大写字母开头,使用 defined? 判断时需要在方法名后添加"()"时,
否则方法名会被当做常数处理。
                                                      36


  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
  若 super 可被调用,则返回真, 具体返回值为字符串"super"。

  class Base
      def foo
      end
  end

  class Derived < Base
     def foo
         puts defined? super
     end
                                37


      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
                                                        38




第四章 基本类型

§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"
  => "first"
  irb(main):019:0> b[1] = "second"
  => "second"
  irb(main):020:0> b
  => ["first", "second"]


数组可以使用 [] 来索引,其实 [] 是 Array 类的一个方法,它甚至可以被子类覆盖
(overridden)。Ruby 中比较有趣的是有多种对数组的索引方法,你可以用负数来索引数组。
负数表示从尾部开始,例如索引为-1 表示最后一个元素,索引为-2 表示倒数第二个元素,以
                                                                     39


此类推。

   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]


你甚至可以用一个范围来索引数组,.. 表示包含尾部元素,... 表示不包含尾部元素。

   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" }
                                                                   40


   => {"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
   => {"third"=>"Tom", "second"=>"Mike", 100=>"Henry", "first"=>"Amy",
"nine"=>"Rose"}




§4.3 Number

  Ruby 支持整数类型和浮点数类型。整数可以是任意长度(这个长度只和内存大小有关)。
                        超出这个范围的整数被视为 Bignum 类的对象。
在一定范围内的整数被视为 Fixnum 类的对象。

   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
                                                            41


  irb(main):003:0> 0x10
  => 16
  irb(main):004:0> 0b10000
  => 16


  一个数中间可以用下划线连接,下划线自动被忽略。

  irb(main):005:0> 123_456_789
  => 123456789


  可以使用"?\C-x"或"?\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} "
  irb(main):054:0> %Q/ #{50*50} /
  => " 2500 "


  %q 和 %Q 后面的第一个字符为分隔符。二哥分隔符之间的字符被认为一个是字符串。
但是如果这个分隔符是 [ { <, 那么结束标志为匹配的 ] } >。
                                                                             42


   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
   => ["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 时条件为假,直观地看,就是表示元素需要位于范围之中。
                                                           43


   可以使用 === 来测试一个元素是否在某个范围:

   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"
   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 就指变量名本身。
                                                        44


    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*[a-f]/
  => /\s*[a-f]/
  irb(main):104:0>   a.class
  => Regexp
  irb(main):105:0>   b = %r{\s*[a-f]}
  => /\s*[a-f]/
  irb(main):106:0>   b.class
  => Regexp
  irb(main):107:0>   c = Regexp.new('\s*[a-f]')
  => /\s*[a-f]/
  irb(main):108:0>   c.class
  => Regexp
                                                  45




   你可以使用 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
  irb(main):122:0> puts $`
  This is
  => nil
                                              46




第五章 代码块和迭代器

§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 具体解释)
 当关心边际(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" }
                                                        47




    前两种方法是等价的,而第三种方法,用 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

  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

  pr = lambda { next }
                                                48


   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 发展过程中,代
码块的用途在后来已经得到了很大的增强,从最初的循环抽象到任何事情。可以将那些进行
迭代操作的方法叫做迭代器,但如果将所有带块方法的调用过程都看作迭带器的话,并不合
适而且概念上会引起混乱




§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
                                             49


   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
   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
                                                   50


        while f1 <= max
            yield f1
            f1, f2 = f2, f1+f2
        end
  end

  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
     def initialize(&block)
         @block = block
     end

        def factorial(max)
                                                     51


              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"

  => nil
同样, 和 case 语句也有返回值, 和 case 语句的返回值就是 if 和 case 中最后一个执行语句
   if                  if
的值。
  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."
                                                        52




§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
  => 14



  对于运算符(+ - * / % ** & | ^ << >> && ||),Ruby 有相应形式的赋值运算符缩写
  形式+=, -=等。

  运算符优先级:
                           ::
                           []
                 +(一元) -(一元) ! ~
                          * / %
                           + -
                          << >>
                            &
                           | ^
                       > >= < <=
                 <=> == === != =~ !~
                           &&
                           ||
                          .. …
                           ?:
         = += -= *= /=(所有的赋值运算符缩写)
                           not
                         and or
                                             53




  以下运算符不能作为方法调用,也就是说不能改变以下运算符的含义:
                     …
                      !
                    not
                     &&
                    And
                     ||
                     Or
                     ::
                      =
        += -= *= /=(所有的赋值运算符
                   缩写)
                     ?:




§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`


  执行结果为:
    Command date success.
    Mon Jan 15 21:48:16 CST 2007
    Command uname success.
                                             54


     Linux




§6.3 赋值运算符

  to-do 定义。

  赋值运算的返回值就是左值的值,所以可以进行链式赋值。
    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
     irb(main):006:0> t = Test.new
     => #<Test:0x2e20568>
     irb(main):007:0> t.num = 10
     => 10
                                                55




§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


 在对数组进行并行赋值时可以使用*,*出现在左边最后一个变量时,表示将数组中所有
 剩余的值赋给这个变量。

 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
                                                                     56


   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




§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?’ 。
                                                      57


和其他程序设计语言一样,and 和 && 代表与关系。
or 和 || 代表或关系。
Not 和 ! 代表非关系。

如果参数没有定义 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? $_
  => "global-variable"
  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
  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
                                                   58




如果为了代码紧凑而将代码放到同一行则不能省略 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


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'
                                                 59


          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


另一个例子:
  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 类的实例(to-do)
===的含义为为参数所提供是否为实例的类或父类。
   case shape
   when Square, Rectangle
   # ...
   when Circle
   # ...
   when Triangle
   # ...
   else
   # ...
   end
                                                         60




§6.9 循环


§6.9.1 Loop

  Loop 循环始终执行其后的方法块,直到 break 退出。
    x = 0
    loop do
        x += 1
        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!"
                                                                 61


   end
执行结果为:
  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 循环。例如
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 。
                                          62




§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
      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
                                                 63


   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


   执行结果为:
   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
                                                  64


   执行结果为:
   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
       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
                                                65




  执行结果为:
  Inner 2
  Outer 1
  Inner 2
  Outer 2




§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



第七章 方法
  Ruby 中的方法使用关键字 def 来定义。方法名应该以小写字母开始,如果你使用大写
                                                  66


字母开始,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 中没有全局函数。虽然
Ruby 中可以象全局函数一样定义和使用方法,但是你应当明白,Ruby 中的方法总是会从属
于某一个对象。
   看到这里,细心的读者会提出一个问题,如果在顶层定义一个方法,那么这个方法属于
谁?
   def meth
   end


  在本书中,我们多次说,Ruby 是一种面向对象的语言,在 Ruby 中的一切都是对象。
那么 meth 方法从属于什么对象呢?我们看一个例子:

  public #为什么使用 public 看后边的解释
  def meth
      puts self.class
  end

  puts self.class
  self.meth
                                                 67


  执行结果为:
  Object
  Object


   在顶层,  当我们定义方法时,  将自动将我们定义的方法作为 Object 类的私有实例方法。
所以这些方法可以在任何地方调用。所以我们可以在任何地方使用 Object 类的任何方法和
Object 类所包含的模块中的任何方法,例如 Kernel 模块中的方法在任何地方可以随意使
用。
   上面的例子中,meth 将作为 Object 类的私有方法,所以我们使用 public 改变它的
存取属性,否则 self.meth 将会产生无法访问私有对象的错误。



   在 Ruby 语言中,方法是存放到 Hash 表中,而键值就是方法名。定义一个方法就是在
Hash 表中添加一项的过程,所以,后定义的同名方法就会替换掉之前定义的方法。
   def meth
       puts "first"
   end

  meth

  def meth
      puts "second"
  end

  meth


  执行结果为:
  first
  second


  Ruby 语言支持缺省参数,但不支持方法重载。方法重载会加重解释器语法解析的复杂
度,影响执行速度。C++的选择是二者都支持,而 Java 的选择刚好与 Ruby 相反,即支持
方法重载,而不支持缺省参数。

  方法名可以以问号“?”,叹号“!”,等于号“=”结尾。这样的方法有约定的含义。以
问号结尾的方法返回布尔值,以叹号结尾的方法表示会改变调用者的内部数据,以等于号结
尾的方法表示可以作为左值。问号“?”,叹号“!”,等于号“=”是方法名的一部分。
  (to-do 例子)

  当一个方法被调用时,运行时环境按照如下顺序搜索:
    1、 在当前对象的特殊方法中搜索
    2、 在当前对象类中定义的实例方法中搜索
    3、 在当前对象所包含的模块中搜索
    4、 在当前对象所在类的父类中搜索
                                                 68


     5、 在当前对象所在类的父类所包含的模块中搜索
     6、 继续 4 和 5 的过程,直到顶层 Object 类




§7.1 运算符重定义

    Ruby 支持重定义运算符,下表列出了 Ruby 支持重定义的运算符和他们默认的含义:
             运算符                        默认含义
[] []=                        元素索引和赋值
**                            幂运算
! ~                           否定,求补
+ -                           正,负,注意他们的方法名为+@,-@以便和
                              加减运算区分
* / %                         乘法,除法,取余运算
+ -                           加减运算
>> <<                         左移,右移
& | ^                         按位与,按位或,按位异或
<= < > >=                     比较运算
===                           Case 表达式中的相等测试
<=>                           范围测试
== !=                         正则表达式匹配相等和不等测试
=~ !~                         相等和不等测试
    注意,!=和!~ 作为==和=~的否定形式,并没有相应的方法。他们经过语法分析后将被
转化为肯定形式调用。例如:
    a != b 实际是调用 !(a==b),同样,a !~ b 将被转化为 !(a=~b)。


§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
                                                                 69


   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




§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 /, '') }
                                                      70




执行结果为:
  no block
  block


如果方法的最后一个参数以&开始,表示方法后连接的块会被转化为 Proc 对象,然后传递
给这个参数。
  def meth1(count, &block)
      value = 1
      1.upto(count) do | i |
          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
                                           71


  使用 return 语句时返回值可以是多个,这时候返回值会被转化为一个数组,你可以用
多重赋值的形式来使用这个返回值。
  def meth1
  return "meth1", 6
  end

  a, b = meth1
                                                                 72




第八章 模块
模块提供了一种组织常量,类和方法的手段。你可以使用模块来提供一个名字空间以避免名
字冲突,你也可以使用模块来提供 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 方法呢?

  我们可以使用模块机制来解决这样的名字冲突。定义一个模块相当于定义了一个名字空
间,名字空间内的元素在全局空间并不直接可见。

   开发者 A 定义模块 A_FILE,
     module A_FILE
         def print_version
         # …
         end
     end
   开发者 B 定义模块 A_FILE,
     module B_FILE
         def print_version
         # …
         end
     end
                                                                    73


  这样对于开发者 C,可以这样使用 print_version
    require „A‟
    require „B‟
    A. print_version
    B. print_version


  类和模块的区别是,模块不能生成实例,而类不能被 include。


§8.2   mixin

  Mix-in 的意思是混合插入、糅合,就像在冰淇淋中混合多种配料可以做成美味的混合冰
淇淋一样,在类中混合插入各种模块就可以添加相应的功能。模块还有另一个重要的作用,
可以使用模块来实现多继承,可以在类中包含模块,这样模块中的所有方法和类中其他方法
一样可以使用。
  Matz 坚信滥用多重继承会导致继承关系的混乱,因此 Ruby 中不允许使用多重继承。同
时为充分发挥继承功能的优势,   Ruby 支持两种继承关系:1.使用 is-a 语句的继承;2.使用 Mix-in
来共享并继承模块中的功能。

  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 是三个特殊的类。
                                                             74




         Object




         Module




         Class


  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
                                                                    75


    puts    aPerson >= bPerson
    puts    aPerson <= bPerson
    puts    aPerson == bPerson
    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(/[1-9]\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


执行结果为:
  123 234 98 10
  12323498


Enumerable mixin 中含 有 许多 与集 合 遍历 查找 相 关的 方法 , 许多 标准 类 也使 用了
                                                                 76


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 技术通常称为单件,是一种常见的设计技术,它保证在系统的
某个类在任一时刻最多只有一个实例在运行。你可以参见设计模式这本书获得有关单件技术
更详细的信息。在 Ruby 中,使用 Singleton Mix-in,你可以很容易的实现单件类。

     在单件类中不能使用 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
                                                        77


   a.data = 8
   puts b.data


执行结果为:
  #<SingletonClassTest:0x2ab9360>
  #<SingletonClassTest:0x2ab9360>
  8


   可以看到,a 和 b 其实指向同一个对象。




§8.4 Require, load 和 include

在 Ruby 中,可以使用 load 和 require 来包含另一个文件。每次运行到 load 时,load 后的
文件会被载入并执行。
   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 已经加载过的文件。但如
                                                           78


果 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 用于加载配置文件。




第九章 异常
     异常(Exception)顾名思义,指的是在程序处理过程中遇到不能处理的非预期条件,
   它会打乱正常的执行流程。
     许多种类的错误会触发异常,      典型的异常例如整数除零,Ruby 提供了一套处理异常
   的机制。如果错误发生,方法将创建一个异常对象并把它抛出到运行时系统。创建一个
   异常对象并将其交给运行时系统叫做抛出异常。异常对象包含了异常的信息,包括异常
   类型,异常发生时的堆栈状态等。运行时系统负责找到一些代码处理这个错误。
     当某个方法抛出一个异常后,运行时系统需要找到一些代码来处理这个异常。运行
   时系统从发生异常的代码处开始,依次查找调用堆栈,直到找到相应的代码处理这个异
   常,然后继续执行其下的代码。注意处理异常过后程序不会返回到异常抛出处继续往下
                                                     79


   执行。
     和传统错误管理技术相比,使用异常来管理错误具有如下优点:
      将正常处理逻辑与错误处理逻辑分开
      错误沿着调用堆栈向上传递,异常处理的代码简洁容易理解
      错误具有相应得类型,可以使用类型来区分不同的错误


§9.1 异常处理

   Ruby 处理异常的语法如下:

   begin
   # 正常处理流程代码
   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 预定义了一个层次结构用来处理常见的异常。
(to-do Ruby 异常层次图)

   在捕捉异常时,可以使用多个 rescue 语句:

   begin
                                                             80


   # 正常处理流程代码
   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
       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 永远不会被执行到。
                                                                  81


  当抛出一个异常时,可以使用 Ruby 内置的异常类,也可以自定义自己的异常类。如果
使用自定义的异常类,那么这个异常类应该是 StandardError 类直接或间接的子类。如果
不这么做,那么默认情况下,这个异常不能正确地被捕捉。

   在 rescue 处理逻辑之后可以加一个 else 子句,只有没有任何异常发生的时候程序才
会运行到那里,这种用法比较少见。
begin
      # 正常处理流程代码
      rescue ExceptionClass1 => exception1
      # 处理当发生 ExceptionClass1 类型的异常
      rescue ExceptionClass2 => exception2
      # 处理当发生 ExceptionClas21 类型的异常
      rescue
      # 处理其他任何异常
      else
      # 没有任何异常
      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
                                                         82


def meth2(skip)
    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]
    rescue RuntimeError => ex
        puts ex.backtrace
    end
end
                                                           83




   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 语句处理异常,那么这个异常会交给运行时环境。运行时
环境会输入异常并中止程序的执行。
  另外,全局变量“$!”保存了最近抛出的异常对象,而“$@”保存了最近发生的异常发
生异常的调用栈信息。

  除了循环以外,还可以在 rescue 部分中使用 retry。这时将从 begin 表达式开始重
新执行。使用 retry 可以在某处理过程成功之前,一直循环该处理过程。

   begin
     do_something # exception raised
   rescue
     # handles error
     retry # restart from beginning
   end


   若 在 rescue 部 分 、 迭 代 器 块 或 for 语 句 之 外 使 用 retry 的 话 会 引 发
LocalJumpError 异常。
                                                     84



§9.2 定义异常类

  Exception 类提供了堆栈和异常的描述信息,在自定义的异常类中,你可以添加额外
的信息。自定义的异常类应该是 StandardError 类直接或间接的子类。否则这个异常可能
不会正确地被捕捉。

   看下面的例子,以下代码位于 MyExceptionTest.rb 中。

   class MyException < RuntimeError
   end

   begin
       raise MyException, "MyException Testing..."
   rescue MyException => ex
       puts ex.to_s
       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(n-1)
   end

   catch(:done) {
      method1(3)
      puts "Can not reach here!"
                                                        85


  }
  puts "Reach here!"


  执行结果:
    3
    2
    1
    0
    Reach here!
  上面的例子中,    我们首先用 “catch(:done)”标注了一个块, 当执行到“throw :done”
时,中止当前“catch(:done)”所标注的块,处理流程继续向下处理。
  在 catch/throw 中,当碰到 throw 时,Ruby 会展开调用堆栈来查找匹配的 catch,
当找到后,堆栈会被展开,catch 块剩余的代码不会被执行。
  Ruby 中没有 goto 语句,但你可以使用 catch/throw 或异常来实现 goto 的功能。



第十章 多任务处理
     当你要同时处理多件任务时 Ruby 提供了两种基本方式,一种方式是在一个程序协
  调需要同时执行的任务,使用多线程处理需要同时执行的操作。另一种方式是将需要同
  时执行的操作分散到多个程序中使用多进程。
     按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。
  一般来说,我们把正在计算机中执行的程序叫做“进程”       (Process),而所谓“线程”
  (Thread),是“进程”中某个单一顺序的控制流。
     线程之间独立运行,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变
  量。但是与进程相比,线程之间的隔离程度要小。它们彼此共用变量,也有可能会同时
  操作一片内存。
     一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如 CPU、内
  存、文件等等)   ,而将线程分配到某个 CPU 上执行。一个进程可以拥有多个线程,此时,
  如果进程运行在多处理器的机器上,    它就可以同时使用多个 CPU 来执行各个线程,    达到
  最大程度的并行,以提高效率;同时,即使是在单 CPU 的机器上,采用多线程模型来设
  计程序,可以使设计更简洁、功能更完备,程序的执行效率也更高。例如采用多个线程
  响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进程模型来实现,而
  与后者相比,线程的上下文切换开销就要小多了。
     在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器以
  及减小上下文切换开销。


§10.1 多线程处理

  Ruby 的多线程是用户级多线程,这样使得 Ruby 的多线程移植非常容易,你并不需关心
具体的操作系统;这样做也使线程容易控制,程序不容易产生死锁这类严重的线程问题。
但是同时,由于 Ruby 的多线程并不是真正意义上的操作系统级多线程,不管代码使用了多
少个 Thread 类的实例,都只会在启动解释器这一个进程内执行,由 Ruby 解释器进行具体的
                                                                   86


线程切换管理,其效率要低于由操作系统管理线程的效率,且不能使用多个 CPU。
   在 Ruby 中同时做多件事最简单的方式就是使用 Thread 类,Thread 类提供了一种高效和
轻量级的手段来同时处理多件任务。
   Thread 类由 Ruby 解释器具体实现,提供了一种同时处理多个任务的方法, Thread 类实
现的并不是操作系统级多线程。
   Ruby 多线程的优点和缺点同样明显,     缺点是效率不如操作系统级多线程,    不能使用多个
CPU,但其优点也很明显,即可移植性很高。这就需要设计人员综合考虑。




§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 方法等待线程结束,程序运行结果和我们预期结果相符。

   另一个例子:
   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" }
  执行没有任何输出,因为主程序执行完毕杀死两个线程的时候这两个线程没有运行到输
出语句。
                                                                  87


   也可以给 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
   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
                                                                       88


   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


   上面的例子中使用 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" }
  b = Thread.new { puts "Thead B" }
                                    89


c = Thread.new { puts "Thead C" }
b.join
c.join
                                                    90


执行结果为:
  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 开关,如果抛出了
未被捕获的异常,程序将结束,所有的线程都将被终止。

   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" }
                                                 91


  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"
  }
  b = Thread.new {
     print "x";
     Thread.pass;
     print "y";
     Thread.pass;
     print "z"
  }

  a.join
  b.join


  执行结果为:
  axbycz
                                                         92


  上面的例子中,我们使用 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 方法等待线程结束并打印出线程执行的返回值,线程
执行的返回值就是线程执行的最后一个语句的值。


§10.1.5 线程同步

  因为线程共享内存空间,所以可以使用普通的变量完成线程间的数据交换工作。如果有
多个线程同时工作,请确保它们之间不存在互相等待以到达某一点或完成的情况。如果操作
错误,可能会导致死锁状态,两个线程都无法完成,因为它们都在相互等待。
  使用线程的另一个常见问题是竞争状态。如果一个线程正在将数据写入文件,而另一个
线程正在从该文件中读取数据,如果读取线程快于写入线程,则将返回无法预料的结果。这
种情况称为竞争状态。

  有时候在运行程序中,一些你认为不需要同步的地方也会出现资源同步的问题:
  class Counter
  attr_reader :number

        def initialize
           @number = 0
  end

        def plus
            @number += 1
        end
                                               93


  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


  这里或许大家会奇怪,为什么 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
               @number += 1
           end
                                                      94


        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
            end
        end
  end

  c = Counter.new

  t1 = Thread.new { 10000.times { c.plus } }
                                                             95


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

puts c.number


执行结果为:
20000


也可以将使用 extend 方法将 Monitor 模块的方法引入完成同步操作。

require 'monitor'
                                                                96




  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 是 mutual-exclusion lock(互斥锁)的简称。它实现了一种简单的信号量
机制,用来协调并发线程对共享数据的访问。

                                      。
  在多线程并行访问共享数据时,可以使用下列代码(m 是 Mutex 的实例)

  begin
     m.lock
     # 访问受 m 保护的共享数据
  ensure
     m.unlock
  end


  Mutex 类有个 synchronize 方法可以简化这一过程。
  m.synchronize {
      # 访问受 m 保护的共享数据
  }
                                            97


  若对 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 }
     }
  }

  t2 = Thread.new {
     m.synchronize {
         10000.times { c.plus }
     }
  }

  t1.join
  t2.join
  puts c.number


  输出结果为:
  20000


   从上一节我们可以知道,若此程序中不使用 Mutex 加以保护的话,因为时机问题,在一
个线程读取 number 的数值后还没来得及进行赋值的时候,另一个线程可能已经改变了
number 的数值。这样,通过 Mutex 互斥锁,我们实现了对共享数据的正确使用。

  Mutex 主要方法如下:
                                                  98


    new       生成新的互斥锁
   lock       若已经处于加锁状态则会一直等待下去直到解锁
  unlock      执行解锁操作,若有其它等锁的线程则会将他们唤醒
synchronize   执行从获得锁到解锁全过程的迭代器
 try_lock     尝试执行加锁操作,若已处于加锁状态,则返回 false 且不会挂起
  locked?     测试 Mutex 对象是否处于加锁状态


§10.1.5.3 Queue
  Queue 就像一条读写数据的管道。提供数据的线程在一边写入数据,而读取数据的线程
则在另一边读出数据。若 Queue 中没有可供读取的数据时,读取数据的线程会挂起等待数据
的到来。
  下面是一个使用 Queue 的简单例子:

   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 来解决生产者-消费者问题。生产者-消费者问题是一个经典的进
                                            99


程同步问题,该问题最早由 Dijkstra 提出,用以演示他提出的信号量机制。生产者线程生产
物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中拿走产
品。生产者线程在缓冲区满时必须等待,直到缓冲区有空间才继续生产。消费者线程在缓冲
区空时必须等待,直到缓冲区中有产品才能继续读取。在这个问题上需要考虑缓冲区满和缓
冲区空的情况以及竞争条件。

  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 有几种方法创建真正的操作系统级的进程。


§10.2.1 进程创建

§10.2.1.1 system 方法和反引号
     可以使用反引号或 system 去执行一个命令。
     system(“tar czf samples.tar.gz .”)
     result = `date`
                                                          100


    使用 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


§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 方法就无法使用。
                                                                                101




第十一章 基本 I/O 操作
  I/O 是 Input/Output 的缩写, 类 IO 是所有 IO 处理的基类,定义了 I/O 操作的接口。I/O
流可以是单向也可以是双向。
  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”表示读写。

    最后记得关闭打开的文件,确保所有被缓冲的数据被写入文件,所有相关的资源被释放。

  我们也可以使用 File.open 来打开文件,open 和 new 的不同是 open 可以使用其后
的代码块而 new 方法则返回一个 File 类的实例。
  File.open("testfile", "r") do |file|
  # ... process the file
  end
                                                       102


  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 表示不同系操作统的路径分割符。


§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"
                                                     103


  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


  输出结果为:
  ...This
  ...is
  ...a
  ...dog!


§11.4 Socket

    使用 Ruby 处理网络十分灵活,Ruby 提供了一系列的库来支持 TCP,
                                         UDP,HTTP,SOCKS,
Unix 套接字和各种各样的网络协议。所有的 Socket 的都是 IO 直接或间接的子类。
    (to-do 图)

  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!"
                                                          104




第十二章 反射和对象空间


§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.22044604925031e-016
  1.79769313486232e+308
  2.2250738585072e-308
  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


each_object 列举系统中的所有指定的对象并返回对象的数目,上述代码列出了系统中所
有的数值型数据,除了我们的数据外还有一些系统预定义的常量。输出结果在你的机器上或
许会稍有不同。
注意 each_ojbect 方法并不列举 Fixnum, Symbol, true, false 和 nil 型的数据。

   a = 102.7
                                                                 105


   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.22044604925031e-016
  1.79769313486232e+308
  2.2250738585072e-308
  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 模块也可以给对象提供析构方法,这个方法在对象被垃圾回收器摧毁时被
调用。
  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
                                                                 106




§12.2 察看类和对象的状态

  当找到一个感兴趣的对象,如何察看它的内部状态?毕竟,我们往往对它的内部状态更
感兴趣。在静态语言中,对象的类型取决于对象的所属类,察看对象所属的类就可以得到对
象支持的方法。和静态语言不同,Ruby 支持更为自由的对象。
  (to-do 解释 Ruby 对象的自由性)

   可以查看一个对象支持那些方法:
   str = “123” #创建一个 String 对象
   list = str.methods
   list[0..3] => ["lstrip!", "to_sym", "scan", "instance_variables"]


   可以查看某个对象是否支持特定的方法:
   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
                                                                  107




      protected
      def protected_method
      end

      public
      def public_method
          @instance_variable = 100
          i = 1
          j = 2
          local_variables
      end

      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
                                                              108


这里我们可以通过 private_instance_methods,protected_instance_methods,
public_instance_methods,singleton_methods 方法分别访问类的私有方法,保护
方法,公有方法和类方法。参数 false 表示不列出父类的方法,如果参数为 true 那么父类
的相关方法也会被列出。class_variables 可以访问类的类变量,constants 类出类的
包     括     父     类     的     所    有     常     量    ,    所      以
InfoTest.constants-InfoTest.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 中,有多种方法可以实现方法的动态调用。




§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!"
                                                        109


  执行结果为:
  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}"
        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
                                                                  110


            2 * @side
      end

      def initialize(side)
          @side = side
      end
end


a = Double.instance_method(:get_value) #返回一个 UnboundMethod 对象
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
                                                                 111



§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


   可以看到上述代码中,@value 在 binding1 所指明的上下文环境中值为 10,在
binding2 所指明的上下文环境中值为 Binding Test。当 eval 方法不提供 binding 参
数时,在当前上下文环境中@value 并未定义,值为 nil。




§12.3.4 性能

  从上面我们可以看到,我们有多种实现动态方法调用的方式,但是请注意,在这多种方
式中,使用 eval 方法最慢。Ruby 提供了一个 Benchmark 模块可以用来报告代码的执行时间,
衡量代码的性能。以下是使用 Benchmark 模块来显示不同动态方法调用的执行时间:
  require 'benchmark'
                                                               112


  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 方法的执行时间远远慢于其他两种。




§12.4 Hook 和回调方法


§12.4.1 什么是 Hook

    最早是在操作系统中出现的 Hook 概念,在 Unix/Linux/Windows 中 Hook 的概念类似,
Hook 的目的在于允许用户在系统执行原有流程的过程中,       插入自己的代码处理一些额外的事
情。典型的 Hook 就是使用实现自己功能的方法替换原有的方法,在所需要的额外处理完成
之后,又恢复原有方法的处理流程。
    (to-do Hook 的图)


§12.4.2 Ruby 中的 Hook


  钩子是一种可以跟踪 Ruby 事件的技术,例如设定一个钩子侦测对象创建,设定好后,
钩子在发生对象创建时触发。Ruby 是动态语言,可以在运行时改变类的方法和属性,我们可
以很容易实现这样的功能。

  下面我们来看一个简单的例子,
  module Mod
     alias_method :old_exit, :exit

     def exit(code=0)
                                                   113


              puts "Exiting with #{code}"
              old_exit(code)
        end
  end

  include Mod
  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
                                                      114



§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(to-do ?),一个类
名。只要发生事件,Proc 就会被调用。事件有这么几种:
  c-call     调用 C 语言过程
  c-return   从 C 语言过程中返回
  call       调用一个 Ruby 方法
  class      开始一个类或模块的定义
  end        结束一个类或模块的定义
  line       执行新的一行代码
  raise      发起一个异常
  return     从 Ruby 方法中返回

  class Test
     def test
         a = 1
     end
                                                                  115


  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
     c-call      test.r b:11        new    Class
     c-call      test.rb:11 initialize Object
   c-return       test.rb:11 initialize Object
   c-return       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




§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
                                                               116


        meth1
  end

  def meth3
      meth2
  end

  meth3


  执行结果为:
  test.rb:6:in `meth2'
  test.rb:10:in `meth3'
  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 的内容。
                                                       117




第十三章 序列化和 YAML

§13.1 序列化的概念

  序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,
它将数据流转换为对象。这两个过程结合起来,就使得数据能够被轻松地存储和传输。
  为什么需使用序列化?有两个重要的原因:一个原因是将对象的状态保存在存储介质中,
以便在以后重新创建精确的副本;另一个原因是可以将对象从一个应用程序发送到另一个应
用程序中,远程处理还可以使用序列化将对象从一台机器上的应用程序传递到另一台机器上
的应用程序中。
  一般程序在运行时产生对象,这些对象随着程序的停止运行而消失,我们可以通过序列
化将对象保存下来,这样在程序终止运行后这些对象仍然存在。以后可以在程序再次运行时
通过读取数据重建这些对象,也可以在在其他程序中利用这些保存下来的对象。这种情况下
我们通过使用对象的序列化和反序列化来完成这样的功能。


§13.2 使用序列化


§13.2.1 二进制数据保存

  在 Ruby 中,序列化也称为 marshaling,使用 Marshal 类 R 可以把一个对象转换成字节流,
并把它存在应用程序外。     这样保存的对象可以在以后被其它的实例或者其它的程序读取使用。
  可以使用 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)
                                                           118


 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)
        @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>
                                                                    119



§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
             @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
                                               120




  一些对象不能被保存,如果将要被保存的对象包括其它程序调用,方法体,IO 实例等,
或者单独的一个对象,或者当你保存一个匿名的类或者方法时,一个 TypeError 错误就会产
生。


§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)
  obj = YAML.load(data)
  puts "After: obj = #{obj}"
                                                      121




  执行结果为:
  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 是一个典型的由委员会驱动的庞然大物,它试图成为一种文档格式、数据格式、
消息包格式、安全的 RPC 通道(SOAP)以及一种对象数据库。而且,XML 为每一类型的
访问和操作都提供了大量的 API:DOM、SAX、XSLT、XPATH、JDOM 以及许多不太常见
的接口层。非常了不起的是 XML 完成了所有这些工作;令人失望的是没有一项工作是完美
无缺的。
  YAML 的关注面则比较窄,它只是清晰地表示在动态编程语言(如 Perl、Python、Ruby
等)中所遇到的数据结构以及数据类型。
  YAML 的优点:
   适合人们阅读
   适合与脚本语言交流
   使用宿主语言的内部数据结构
   拥有一致性的信息模型
   使基于流式数据的处理成为可能
   富于表达和可扩展
   容易实现
  YAML 做为一个可移植的对象序列化方案,可以在不同的 Ruby 进程中将对象以普通文
本传递,此外也适合 ruby 与那些支持 YAML 的语言之间交换数据用。
                                                     122




  YAML 中数据主要由序列(sequence),map(有的也叫做 hash)和标量(scalar)来表示。
语法比较简单,易于人们阅读。
   注释由#开头
   序列由"-"开头
   map 用 key:value 的格式
   "---"表示一个 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‟]]


  使用缩进表示不同的层次,序列也可以表示更深层次的嵌套
  -
    -
      - Henry
      - Mike
      - Tom
  在 Ruby 中,
  [[[„Henry‟, „Mike‟, „Tom‟]]]
                                                         123




   混合嵌套序列
  在序列中也可以嵌套其他的 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:
    - teacher
    - male


  在 Ruby 中,
  {„Grant‟ => „student‟, „Tom‟ => [„teacher‟, „male‟ }


   嵌套表
  teacher: Mike
  students:
   Grant:13
   Henry:14


  在 Ruby 中,
  { „teacher‟ => „Mike‟,
     „students‟ => {
         „Grant‟ => „13‟,
         „Henry‟ => „14‟
     }
                                                   124


}


 混合表
grade: 1
teachers:
 Mike:math
 Jane:english
classes:
 -
  Grant:13
  Henry:14
 -
  Jack:13
  Rose:13


在 Ruby 中,
{ „grade‟ => „1‟,
   „teachers‟ =>
   {
       „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‟] } ]


 表中嵌套序列的缩写
当在表中嵌套一个序列时,可以不需要缩进。
                                       125


 Students:
 - „Grant‟
 - „Henry‟


 在 Ruby 中,
 {„Students‟ => [„Grant‟, „Henry‟] }


  插入键(Merge key)
 可以使用插入键在一个表中插入另一个表
 { 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
                                              126



§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


  结果为:
  --- !ruby/object:Person
  age: 15
  gender: male
  name: Tom


  假设现在有一个 person.xml 文件内容为:
  --- !ruby/object:Person
  age: 25
  gender: male
  name: Henry
                                                                  127




    如下代码:
    #从 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 代码可以方便的移植, 也可以方便的对原代码进行分发。   这样带来了灵活性的同时
也带来了巨大的隐患,如何才能防止不安全代码的执行呢?
  我们可以利用 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"
                                                    128


  str1.tainted?        #=> false

  str2 = str1 + “!”
  str2.tainted?        #=> false


  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 级的约束之外,对文件有潜在危险的操作被禁止。


§14.3 3 级

  除了 2 级的约束之外,所有新产生的对象都被标记为 tainted。


§14.4 4 级

  除了 3 级的约束之外,不能改变全局变量的值。
                                               129




第十五章 单元测试

§15.1 什么是单元测试


§15.2 Ruby 单元测试框架




第二部分         内置类与模块
  这一部分我们将列出 Ruby 语言标准内置的类与模块,这些类与模块可以在任何 Ruby 程
序中使用而不需要额外使用 require 语句。




第一章 内置类

§1.1 Array

  Array 类是有序的对象集合。Array 类以整数作为索引,索引从 0 开始,负数索引表示从
尾部逆序开始。例如-1 表示最后一个元素,-2 表示倒数第二个元素,依次类推。
 父类
  Array 类的父类为 Object 类。
 Mix-in
  Enumerable
 类方法
                                                      130



§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 语句紧接的块后,所以“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...
               131


end...
in method...
end...
                   132



§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


§1.15 IO


§1.16 MatchData


§1.17 Method
                        133



§1.18 Module


§1.19 NilClass


§1.20 Numeric


§1.21 Object


§1.22 Proc


§1.23 Process::Status


§1.24 Range


§1.25 Regexp


§1.26 String


§1.27 Struct


§1.28 Struct::Tms


§1.29 Symbol
                      134



§1.30 Thread


§1.31 ThreadGroup


§1.32 Time


§1.33 TrueClass


§1.34 UnboundMethod
                     135




第二章 内置模块

§2.1 Comparable


§2.2 Enumerable


§2.3 Error


§2.4 FileTest


§2.5 GC


§2.6 Kernel


§2.7 Marshal


§2.8 Math


§2.9 ObjectSpace


§2.10 Process


§2.11 Process::GID
                                                                 136



             :Sys
§2.12 Process:


             :UID
§2.13 Process:


§2.14 Signal




all?方法
方法原型:
   enum.all? [ {|obj| block} ]
   方法依次将集合内的每个元素传递给块,如果所有的执行结果都为真那么方法返回
true,任意一次执行结果为假那么方法返回 false。如果忽略了 block 块,则 Ruby 会自动
添加一个{|obj|obj}块。
   %w{ ant bear cat}.all? {|word| word.length >= 3} #=> true
   %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"
                                                            137




  # 找出最长单词的长度
  longest = %w{ cat sheep bear }.inject(0) do |memo,word|
      memo >= word.length ? memo : word.length
  end
  longest                                   #=> 5


这个方法之所以用 inject 这个名称,是因为这个方法有注射动作,即将 initial 及后来
的块计算结果注入到 memo 中。




第三部分            Ruby 语言总结
                                                 138




 书写习惯
  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>

使用分隔符也可以跨越多行:
  string = %q{
     BEGIN {
         puts "===BEGIN==="
     }
  }



        %q           单引号包围的字符串
        %            双引号包围的字符串
        %Q           双引号包围的字符串
        %w         每一个元素都是字符串的数组
        %W         每一个元素都是字符串的数组
        %r             正则表达式
        %x             Shell 命令
                                                            139


 命名规则
  Ruby 中的对象和常量保存了对实际对象的引用,对象自己并没有类型,所谓的对象
的类型是指它引用的实际对象的类型。
  常量可以在类或模块中定义,访问时需要使用类名加上“::” 。

   变量
    在 Ruby 中使用不同的前缀来区分变量的不同类型:
       类型         示例             解释
    全局变量      $foo    全局变量以$开始,未初始化的全局变量值为 nil
    实例变量      @foo    实例变量以@开始,未初始化的实例变量值为 nil
    类变量       @@foo   类变量以@@开始,必须初始化
    局部变量      foo     局部变量以小写字母或下划线开始
    常量        Foo     常量以大写字母开始

   预定义变量
     异常相关
       名称      类型                         解释
    $!     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
                                                        140


    $stderr      IO
    $stdin       IO
    $stdout      IO

       环境相关
        名称             类型                  解释
    $0           String            当前执行应用程序名
    $*           Array             保存了命令行执行的参数
    $”           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
    STDIN               IO
    STDOUT              IO
                                            141


    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 }


 执行结果为:
 This is 0
 This is 1
 This is 2
 This is 3
 This is 4
 This is 5

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:15
posted:2/10/2012
language:
pages:141