Francis's Octopress Blog

A blogging framework for hackers.

ruby参考手册VII

ruby参考手册VII

Ruby FAQ

  1. 一般的问题
  2. 变量、常数、参数
  3. 调用带块方法(迭代器)
  4. 句法
  5. 方法
  6. 类、模块
  7. 内部库
  8. 扩展库
  9. 尚未列出的功能
  10. 日语字符的处理
  11. Ruby的处理系统

一般的问题

  • 1.1 Ruby是什么?
  • 1.2 为什么取名叫Ruby呢?
  • 1.3 请介绍一下Ruby的诞生过程
  • 1.4 哪里有Ruby的安装文件?
  • 1.5 请问Ruby的主页在哪里?
  • 1.6 请问有Ruby邮件列表吗?
  • 1.7 怎么才能看到邮件列表中的老邮件?
  • 1.8 rubyist和ruby hacker的区别是什么?
  • 1.9 它的正确写法是”Ruby”还是”ruby”?
  • 1.10 请介绍一些Ruby的参考书
  • 1.11 我看了手册可还是不明白,该怎么办?
  • 1.12 ruby的性格比较像羊?
  • 1.13 遇到bug时怎么上报?

变量、常数、参数

  • 2.1 将对象赋值给变量或常数时,会先拷贝该对象吗?
  • 2.2 局部变量的作用域是如何划定的?
  • 2.3 何时才能使用局部变量?
  • 2.4 常数的作用域是如何划定的?
  • 2.5 实参是怎么传递给形参的呢?
  • 2.6 将实参赋值给形参之后,对实参本身有什么影响吗?
  • 2.7 若向形参所指对象发送消息的话,可能出现什么结果?
  • 2.8 参数前面的*是什么意思?
  • 2.9 参数前面的&代表什么?
  • 2.10 可以给形参指定默认值吗?
  • 2.11 如何向块传递参数呢?
  • 2.12 为什么变量和常数的值会自己发生变化?
  • 2.13 常数不能被修改吗?

调用带块方法

  • 3.1 什么是”带块的方法调用”?
  • 3.2 怎么将块传递给带块方法?
  • 3.3 如何在主调方法中使用块?
  • 3.4 为什么Proc.new没有生成过程对象呢?

句法

  • 4.1 像:exit这种带:的标识符表示什么?
  • 4.2 如何取得与符号同名的变量的值?
  • 4.3 loop是控制结构吗?
  • 4.4 a +b报错,这是怎么回事儿?
  • 4.5 s = “x”; puts s *10 报错,这是怎么回事儿?
  • 4.6 为什么p {}没有任何显示呢?
  • 4.7 明明有pos=()这样的setter方法,可为什么pos=1时却没有任何反应呢?
  • 4.8 ‘\1’和’\1’有什么不同?
  • 4.9 在p true or true and false中会显示true,但在a=true if true or true and false中却不会把true赋值给a。
  • 4.10 为什么p(nil || “”)什么事儿都没有,可p(nil or “”)却会报错呢?

方法

  • 5.1 向对象发出消息之后,将按照什么顺序来搜索要执行的方法?
  • 5.2 +和-是操作符吗?
  • 5.3 Ruby中有函数吗?
  • 5.4可以在外部使用对象的实例变量吗?
  • 5.5 private和protected有什么不同?
  • 5.6 能不能将实例变量变成public类型的变量?
  • 5.7 怎样指定方法的可见性?
  • 5.8 方法名可以用大写字母开头吗?
  • 5.9 为什么使用super时会出现ArgumentError?
  • 5.10 如何调用上2层的同名方法?
  • 5.11 重定义内部函数时,如何调用原来的函数?
  • 5.12 何谓破环性的方法?
  • 5.13 那些情况下会产生副作用?
  • 5.14 能让方法返回多个值吗?

类、模块

  • 6.1 重定义类时,是否会覆盖原来的定义?
  • 6.2 有类变量吗?
  • 6.3 什么是类的实例变量?
  • 6.4 什么是特殊方法?
  • 6.5 什么是类方法?
  • 6.6 什么是特殊类?
  • 6.7 什么是模块函数?
  • 6.8 类和模块有什么区别?
  • 6.9 模块可以生成子类吗?
  • 6.10 在类定义中定义类方法 和 在顶层中定义类方法 之间有什么不同?
  • 6.11 load和require有什么不同?
  • 6.12 include和extend有什么不同?
  • 6.13 self是什么?
  • 6.14 MatchData中的begin、end分别返回什么?
  • 6.15 如何使用类名来获得类?

内部库

  • 7.1 instance_methods(true)返回什么?
  • 7.2 为什么rand总是生成相同的随机数?
  • 7.3 怎样从0到51中选出5个不重复的随机数呢?
  • 7.4 Fixnum、Symbol、true、nil和false这些立即值与引用有什么不同?
  • 7.5 nil和false有什么不同?
  • 7.6 为什么读入文件并修改之后, 原文件依然没有变化?
  • 7.7 怎样覆盖同名文件?
  • 7.8 写文件后拷贝该文件,但所得副本并不完整,请问原因何在?
  • 7.9 在管道中将字符串传给less后, 为什么看不到结果?
  • 7.10 无法引用的File对象将会何去何从?
  • 7.11 怎样手动关闭文件?
  • 7.12 如何按照更新时间的新旧顺序来排列文件?
  • 7.13 如何获取文件中单词的出现频度?
  • 7.14 为什么条件表达式中的空字符串表示true呢?
  • 7.15 如何按照字典顺序来排列英文字符串数组?
  • 7.16 “abcd”[0]会返回什么?
  • 7.17 怎么把tab变成space?
  • 7.18 如何对反斜线进行转义操作?
  • 7.19 sub和sub!的区别在哪里?
  • 7.20 \Z匹配什么?
  • 7.21 范围对象中的..和…有什么不同?
  • 7.22 有函数指针吗?
  • 7.23 线程和进程fork有何异同?
  • 7.24 如何使用Marshal?
  • 7.25 Ruby有异常处理语句吗?
  • 7.26 如何使用trap?
  • 7.27 如何统计文件的行数?
  • 7.28 怎样把数组转化为哈希表?
  • 7.29 将字符串变为Array时可以使用%w(…),那么将字符串变为Hash时能不能如法炮制呢?
  • 7.30 为何无法捕捉NameError异常呢?
  • 7.31 为什么有succ却没有prev呢

扩展库

  • 8.1 如何使用交互式Ruby?
  • 8.2 有调试器吗?
  • 8.3 怎样在Ruby中使用以C写成的库?
  • 8.4 有Tcl/Tk的接口吗?
  • 8.5 为什么我的Tk不管用?
  • 8.6 有gtk+、xforms的接口吗?
  • 8.7 进行日期计算时需要注意哪些问题?

尚未列出的功能 日语字符的处理

  • 10.1 若包含汉字的脚本输出乱码或无法正常运行时,该如何处理?
  • 10.2 选项-K和$KCODE有什么不同?
  • 10.3 可以使用日语标识符吗?
  • 10.4 如何从包含日语字符的字符串中依次抽出1个字符?
  • 10.5 tr(“あ”,“a”)运作不正常,应如何处置?
  • 10.6 如何对平假名进行排序?
  • 10.7 如何用空白来替代SJIS中从84BF到889F之间的系统相关代码?
  • 10.8 如何进行全角-半角字符的变换?
  • 10.9 关于半角假名的问题
  • 10.10 怎样从包含日语字符的字符串中抽出n字节的内容?
  • 10.11 怎么让日语文本在第n个字处换行?

Ruby的处理系统

  • 11.1 能不能编译Ruby脚本呢?
  • 11.2 有没有Java VM版的Ruby?
  • 11.3 除了original Ruby之外,就没有其他版本吗?
  • 11.4 有没有Ruby用的indent?
  • 11.5 有没有使用本地线程的Ruby?
  • 11.6 GC实在是太慢了,怎么办才好?
  • 11.7 有没有Mac版的Ruby?

  • 一般的问题

  • 1.1 Ruby是什么?

  • 1.2 为什么取名叫Ruby呢?
  • 1.3 请介绍一下Ruby的诞生过程
  • 1.4 哪里有Ruby的安装文件?
  • 1.5 请问Ruby的主页在哪里?
  • 1.6 请问有Ruby邮件列表吗?
  • 1.7 怎么才能看到邮件列表中的老邮件?
  • 1.8 rubyist和ruby hacker的区别是什么?
  • 1.9 它的正确写法是”Ruby”还是”ruby”?
  • 1.10 请介绍一些Ruby的参考书
  • 1.11 我看了手册可还是不明白,该怎么办?
  • 1.12 ruby的性格比较像羊?
  • 1.13 遇到bug时怎么上报?

1.1 Ruby是什么?

一言以蔽之,Ruby是一种

语法简单且功能强大的面向对象的脚本语言。

与perl一样,Ruby的文本处理功能十分强大。当然了它的功能远不止于此,您还可以使用它来开发实用的服务器软件。

Ruby博采众长,吸收了众多语言的优点,却又别具一格。

Ruby的优点主要体现在以下几个方面。

  • 它的语法简单
  • 它具有普通的面向对象功能(类、方法调用等)
  • 它还具有特殊的面向对象功能(Mix-in、特殊方法等)
  • 可重载操作符
  • 具有异常处理功能
  • 调用带块方法(迭代器)和闭包
  • 垃圾回收器
  • 动态载入(取决于系统架构)
  • 可移植性。它可以运行在大部分的UNIX、DOS和Mac上

1.2 为什么取名叫Ruby呢?

松本先生曾经在[ruby-talk:00394]英译稿中讲过取名的经过。

据说当初松本先生一直琢磨着要给这个新语言取个像Perl这样的宝石名字,正好有个同事的诞生石是Ruby,因此就取名叫Ruby了。

后来发现Ruby和Perl真的很投缘,例如pearl诞生石代表6月,而ruby诞生石则代表7月。还有pearl的字体大小是5pt,而ruby则是5.5pt等等。因此松本先生觉得Ruby这个名字很合适,并努力使其成为比Perl更新更好的脚本语言。

松本先生正期待着Ruby取代Perl的那一天早点到来(^^)。 1.3 请介绍一下Ruby的诞生过程

松本先生曾经在[ruby-talk:00382]英译稿中介绍过Ruby的诞生过程。[ruby-list:15997]修改了Ruby的诞生时间。

*

Ruby诞生于1993年2月24日。那天我和同事们聊了聊面向对象语言的可能性问题。我了解Perl(Perl4而非Perl5),但我不喜欢它身上的那股玩具味儿(现在也是如此)。面向对象的脚本语言的前途一片光明。

我觉得Python不能算作真正的面向对象语言,因为它的面向对象特性好像是后加进去的一样。15年来我一直为编程语言而痴狂,我热衷于面向对象编程,但却没有找到一款真正意义上的面向对象的脚本语言。

于是我下定决心自己来开发一个。经过几个月的努力,解释器终于开发成功。然后我又添加了一些自己梦寐以求的东西,如迭代器、异常处理、垃圾回收等。

后来我又采用类库方式添加了Perl的特性。1995年12月,我在日本国内的新闻组上发布了Ruby 0.95版本。

接下来我创建了邮件列表和网站。此后,大家在邮件列表中聊得酣畅淋漓。时至今日,第一个邮件列表中已经积累了14789封邮件。

Ruby 1.0发布于1996年12月,1.1发布于1997年8月。1998年12月,我又发布了安定版1.2和开发版1.3。

1.4 哪里有Ruby的安装文件?

您可以在这里<URL:ftp://ftp.ruby-lang.org/pub/ruby/&gt;%E6%89%BE%E5%88%B0%E6%9C%80%E6%96%B0%E7%89%88%E7%9A%84Ruby%E3%80%82

镜像站点列表如下

您可以在Ruby Binaries中找到cygwin版、mingw版和djgpp版的二进制文件包。

另外,Windows(cygwin)中还为初学者准备了Ruby Entry Package。安装方法请参考面向初学者的Ruby安装说明。 1.5 请问Ruby的主页在哪里?

Ruby的官方网站是<URL:http://www.ruby-lang.org/&gt;%E3%80%82 1.6 请问有Ruby邮件列表吗?

现在有6个正式的Ruby邮件列表。

  • ruby-list
  • ruby-dev
  • ruby-ext
  • ruby-math
  • ruby-talk
  • ruby-core

详情请参考Ruby邮件列表。 1.7 怎么才能看到邮件列表中的老邮件?

<URL:http://blade.nagaokaut.ac.jp/ruby/ruby-list/index.shtml&gt;%E5%92%8C&lt;URL:http://ruby.freak.ne.jp/&gt;%E9%87%8C%E9%9D%A2%E6%9C%89%E6%90%9C%E7%B4%A2%E9%82%AE%E4%BB%B6%E7%94%A8%E7%9A%84%E8%A1%A8%E5%8D%95%E3%80%82

另外,ML Topics中列出了老邮件中的重要话题。 1.8 rubyist和ruby hacker的区别是什么?

松本先生对rubyist和Ruby hacker的定义如下。

rubyist是指那些对Ruby的关心程度超过常人的人。例如

  • 向周围的人宣传Ruby的人
  • 编写Ruby的FAQ的人
  • 在计算机通信组中增加Ruby小组的组长
  • 撰写Ruby书籍的作者
  • 写信鼓励Ruby作者的热心人
  • Ruby作者本人 ^^;;;

而Ruby hacker是指那些在技术层面上对Ruby有所专攻的人。例如

  • Ruby扩展库的作者
  • 修改Ruby中的bug并发布补丁的人
  • djgpp版Ruby或win32版Ruby的作者
  • 用Ruby编写了实用(必须得具备一定规模的)程序的人
  • 用Ruby编写出天书般难懂的脚本的人
  • Ruby作者本人 ^^;;;

等就是Ruby hacker。

这些称号只不过是自我解嘲式的自称,我不会为任何人进行正式的认证。松本先生特别将上述人士列为{rubyist、Ruby hacker},可见其尊敬之情。 1.9 它的正确写法是”Ruby”还是”ruby”?

Ruby的正式写法是”Ruby”,其命令名是”ruby”。另外只要不让人觉得别扭的话,也可以使用ruby来替代Ruby。

但不能把”RUBY”、”ルビー”或”るびー”用作这门语言的名称。

此前曾经有一段时间把”ruby”用作正式名称。 1.10 请介绍一些Ruby的参考书

主要有《オブジェクト指向スクリプト言語Ruby》(译注:日语书名未翻译)[松本行弘/石塚圭树 合著 ASCII出版(ISBN4-7561-3254-5)],其他书目请参考Ruby相关书籍。

至于正则表达式,请参考Jeffrey E. F.Friedl著的《詳説正規表現》(译注:日语书名未翻译)[reilly Japan出版(ISBN4-900900-45-1)]。这本书介绍了各种正则表达式的实现问题,有助于加深您对于Ruby正则表达式的理解。 1.11 我看了手册可还是不明白,该怎么办?

Ruby的基本句法从Ruby1.0以后就没有太大的变化,但却在不断完善和扩充,因此有时文档的更新速度跟不上最新的发展。另外,有人坚持说源代码就是文档,如此种种。

若您有何疑问,请不必顾虑太多,直接到ruby-list中提问即可。Ruby教主松本先生以及各位尊师还有我都会为您排忧解难。

提问时,请写明ruby -v的结果并附带上您的代码(若代码太长的话,只需摘录重要部分即可)。

若您使用的是irb的话,则稍有不同。因为irb自身也存在一些问题,所以您最好先用irb —single-irb重试一下,或者用ruby重新运行一次为好。

虽然搜索ML可以解决您的大部分问题,但因为邮件数量庞大,想找到答案实属不易。为遵从网络礼节(请参考RFC1855的3.1.1、3.1.2),您可以只搜索最近的内容,但是说起来容易,做起来难。况且说不定最近又出现了什么新观点呢。所以您还是壮起胆子来提问吧。 1.12 ruby的性格比较像羊?

羊、蜂鸟、兔子… 1.13 遇到bug时怎么上报?

遇到bug时应该上报到Ruby Bug Tracking System,通常很快就会得到回复。您也可以用邮件将bug的情况上报到ruby-bugs-ja。

上报时,最好能提供ruby的版本和平台信息、错误消息以及能再现bug的脚本和数据。

遇到bug时,通常会显示[BUG]消息,而Ruby也将被强行关闭。此时大部分系统都会生成一个core文件。若您的调试器可用的话,可能还会有backtrace。若您能提供这些信息就更好了。

  1. 变量、常数、参数

  2. 2.1 将对象赋值给变量或常数时,会先拷贝该对象吗?

  3. 2.2 局部变量的作用域是如何划定的?
  4. 2.3 何时才能使用局部变量?
  5. 2.4 常数的作用域是如何划定的?
  6. 2.5 实参是怎么传递给形参的呢?
  7. 2.6 将实参赋值给形参之后,对实参本身有什么影响吗?
  8. 2.7 若向形参所指对象发送消息的话,可能出现什么结果?
  9. 2.8 参数前面的*是什么意思?
  10. 2.9 参数前面的&代表什么?
  11. 2.10 可以给形参指定默认值吗?
  12. 2.11 如何向块传递参数呢?
  13. 2.12 为什么变量和常数的值会自己发生变化?
  14. 2.13 常数不能被修改吗?

2.1 将对象赋值给变量或常数时,会先拷贝该对象吗?

变量和常数都指向一个对象。即使不赋值, 它也是指向nil对象的。赋值操作只不过是让它指向另一个新对象而已。

所以, 赋值时并不会拷贝并生成一个新对象. 而是让赋值表达式左边的变量或常数指向表达式右边的对象。

尽管如此, 可能还是有人不理解. 这也是情有可原的, 因为上面的解释并不能涵盖所有的情况. 实际上, Fixnum、NilClass、 TrueClass、FalseClass以及Symbol类的实例会被变量或常数直接保存, 所以赋值时会被拷贝。其他类的实例都在内存上的其他地方, 变量和常数会指向它们。请参考立即值和使用。 2.2 局部变量的作用域是如何划定的?

顶层、类(模块)定义或方法定义都是彼此独立的作用域。另外, 在块导入新的作用域时, 它还可以使用外侧的局部变量。

块之所以与众不同, 是因为这样能够保证Thread或过程对象中的局部变量的”局部性”。while、until、for是控制结构, 它们不会导入新的作用域。另外, loop是方法, 它的后面跟着块。 2.3 何时才能使用局部变量?

在Ruby解释器运行Ruby脚本时, 它会一次读取整个脚本,然后进行语法分析。若没有语法问题的话, 才会开始执行句法分析中得到的代码。

在进行语法分析时, 只有在遇到局部变量的赋值语句之后, 才能使用它。例如

for i in 1..2 if i == 2 print a else a = 1 end end

把上述代码写入名为test.rb的脚本. 执行该脚本后发生如下错误

test.rb:3: undefined local variable or method `a’ for

<Object:0x40101f4c> (NameError)

from test.rb:1:in `each’ from test.rb:1

当i值为1时,并不会发生错误;当i变成2之后就不行了。这是因为, 在进行语法分析时并不会按照运行时的逻辑顺序来进行, 而只是机械地逐行分析. 在遇到print a语句时, a并未被赋值, 因而无法使用该局部变量. 之后,在运行时因为找不到名为a的方法, 所以发生错误。

相反地, 若使用如下脚本则不会出现错误。

a = 1 if false; print a

=> nil

若您不想因为局部变量的这个特性而费神的话, 我们推荐您在使用局部变量之前, 添加a = nil赋值语句。这样作还有一个好处, 就是可以加快局部变量的使用速度。 2.4 常数的作用域是如何划定的?

类/模块中定义的常数可以用在该类/模块中。

若类/模块定义发生嵌套时, 可在内侧类/模块中使用外侧的常数。

还可以使用超类以及包含模块中的常数。

因为顶层中定义的常数已经被添加到Object类中, 所以您可以在所有的类/模块中使用顶层中的常数。

若遇到无法直接使用的常数时, 可以使用 类/模块名+::操作符+常数名 的方式来使用它。 2.5 实参是怎么传递给形参的呢?

方法调用时, 会把实参赋值给形参。请您参考向变量进行赋值来了解Ruby中赋值的含义。若实参中的对象包含可以改变自身状态的方法时,就必须注意其副作用(当然了,也有可能不是副作用)了。请参考破坏性的方法。 2.6 将实参赋值给形参之后,对实参本身有什么影响吗?

形参是局部变量, 对其进行赋值之后, 它就会指向其他对象. 仅此而已, 它并不会对原来的实参有什么影响。 2.7 若向形参所指对象发送消息的话,可能出现什么结果?

形参所指对象实际上就是实参所指对象. 若该对象接到消息时状态发生变化的话,将会影响到主调方。请参考破坏性的方法。 2.8 参数前面的*是什么意思?

各位C语言大侠请看好, 这可不是什么指针。在Ruby的参数前面添加一个*表示, 它可以接受以数组形式传来的不定量的参数。

def foo(*all) for e in all print e, “ ” end end

foo(1, 2, 3)

=> 1 2 3

另外,如果在方法调用中传了一个带*的数组, 则表示先展开数组然后再进行传递。

a = [1, 2, 3] foo(*a)

现在只能在以下部分的尾部使用*

  1. 多重赋值的左边
  2. 多重赋值的右边
  3. 参数列表(定义方法时)
  4. 参数列表(调用方法时)
  5. case的when部分

下面是在第(1)种形式中使用*的例子

x, *y = [7, 8, 9]

上面的代码相当于x = 7、y = [8, 9]。另外,下面的代码

x, = [7, 8, 9]

也是合法的, 此时x = 7. 而

x = [7, 8, 9]

则表示x = [7, 8, 9]。 2.9 参数前面的&代表什么?

在参数前面添加&之后,就可以像使用块那样来传递/接收过程对象。它只能位于参数列表的末尾。 2.10 可以给形参指定默认值吗?

可以。

在调用函数时,才会计算该默认值。您可以使用任意表达式来设定Ruby的默认值(C++只能使用编译时的常数). 调用方法时,会在方法的作用域内计算默认值。 2.11 如何向块传递参数呢?

在块内部的前端,使用||将形参括起来之后, 就可以使用实参进行多重赋值了。该形参只是普通的局部变量, 若块的外侧已经有同名参数时, 块参数的作用域将扩大到块外侧, 请留意这个问题。 2.12 为什么变量和常数的值会自己发生变化?

请看下例。

A = a = b = “abc”; b << “d”; print a, “ ”, A

=> abcd abcd

对变量或常数进行赋值, 是为了以后通过它们来使用对象。这并不是将对象本身赋值给变量或常数, 而只是让它们记住对该对象的引用。变量可以修改这个引用来指向其他的对象, 而常数却不能修改引用。

对变量或常数使用方法时, 实际上就是对它们所指的对象使用该方法。在上例中, <<方法修改了对象的状态,所以引发了”非预期”的结果。若该对象是数值的话, 就不会发生这种问题, 因为数值没有修改其自身状态的方法。若对数值使用方法时, 将返回新的对象。

这个例子虽然是用字符串来作演示的, 但就算使用带有可修改自身状态的方法的那些对象, 如数组或哈希表等来试验的话, 效果也是一样的。 2.13 常数不能被修改吗?

若想让指向某对象的常数转而指向其他对象时, 就会出现warning。

若该对象带有破坏性的方法的话, 则可以修改该对象的内容。

  1. 带块的方法调用

  2. 3.1 什么是”带块的方法调用”?

  3. 3.2 怎么将块传递给带块方法?
  4. 3.3 如何在主调方法中使用块?
  5. 3.4 为什么Proc.new没有生成过程对象呢?

3.1 什么是”带块的方法调用”?

有些方法允许在调用它的过程中添加块或者过程对象, 这种特殊的方法调用就是”带块的方法调用”。

这原本是为了对控制结构(特别是循环)进行抽象而设置的功能, 因此有时也被称作迭代器. 当然了, 若您只想调用块而不进行iterate(迭代)操作时,也可以使用它.

下例中就用到了迭代器。

data = [1, 2, 3] data.each do |i| print i, “\n” end

它会输出如下内容。

$ ruby test.rb 1 2 3

也就是说,do和end之间的块被传递给方法, 供其差遣。each方法分别为data中的每个元素来执行块的内容。

用C语言来改写的话,就是

int data[3] = {1, 2, 3}; int i; for (i = 0; i < 3; i++) { printf(“%d\n”, data[i]); }

用for来编写代码时, 必须自己进行迭代处理. 相反地, 使用带块的方法调用时, 则由方法负责处理, 这大大减少了因误判循环边界而导致bug的可能性。

另外, 除了do…end之外, 您还可以使用{…}。

data = [1, 2, 3] data.each { |i| print i, “\n” }

这段代码与前面的完全等效。但这并不标明do…end与{…}完全等效。例如

foobar a, b do .. end # 此时foobar被看做是带块的方法 foobar a, b { .. } # 而此时 b被看做是带块的方法

这说明{ }的结合力大于do块。 3.2 怎么将块传递给带块方法?

如果想将块传递给带块方法, 只需要将块放在方法后面即可. 另外, 还可以在表示过程对象的变量/常数前添加&, 并将其作为参数传递给方法即可。 3.3 如何在主调方法中使用块?

有3种方式可以让您在方法中使用块. 它们分别是yield控制结构、块参数和Proc.new。(在由C语言写成的扩展库中,需要使用rb_yield)

使用yield时, yield后面的参数会被传递给块, 然后执行块的内容。

块参数是指,插在方法定义中的参数列表末尾的 形如&method的参数. 可以在方法中,这样method.call(args…)来进行调用。

使用Proc.new时, 它会接管传递给方法的块, 并以块的内容为范本生成一个过程对象。proc或lamda也是一样。

def a (&b) yield b.call Proc.new.call proc.call lambda.call end a{print “test\n”}

3.4 为什么Proc.new没有生成过程对象呢?

若没有给出块的话, Proc.new是不会生成过程对象的, 而且还会引发错误。在方法定义中插入Proc.new时, 一般都假定在方法调用时会传过来一个块。

  1. 句法

  2. 4.1 像:exit这种带:的标识符表示什么?

  3. 4.2 如何取得与符号同名的变量的值?
  4. 4.3 loop是控制结构吗?
  5. 4.4 a +b报错,这是怎么回事儿?
  6. 4.5 s = “x”; puts s *10 报错,这是怎么回事儿?
  7. 4.6 为什么p {}没有任何显示呢?
  8. 4.7 明明有pos=()这样的setter方法,可为什么pos=1时却没有任何反应呢?
  9. 4.8 ‘\1’和’\1’有什么不同?
  10. 4.9 在p true or true and false中会显示true,但在a=true if true or true and false中却不会把true赋值给a。
  11. 4.10 为什么p(nil || “”)什么事儿都没有,可p(nil or “”)却会报错呢?

4.1 像:exit这种带:的标识符表示什么?

它叫做符号对象,它与标识符之间是1对1的关系。您也可以使用”exit”.intern来得到它。在catch, throw, autoload等方法中,既可以使用字符串参数,又可以使用符号参数。 4.2 如何取得与符号同名的变量的值?

在symbol的作用域内,使用eval((:symbol).id2name)来取值。

a = ‘This is the content of “a”’ b = eval(:a.id2name) a.id == b.id

4.3 loop是控制结构吗?

不,它是方法。该块会导入新的局部变量的作用域。 4.4 a +b报错,这是怎么回事儿?

它会被解释成a(+b)。+的两侧要么都有空格,要么就都没有。 4.5 s = “x”; puts s *10 报错,这是怎么回事儿?

puts s 10会被解释成s(10)的方法调用,所以要么s*10这样,要么s * 10这样。 4.6 为什么p {}没有任何显示呢?

{}会被解释成块,而并非哈希表的构造函数。所以您需要使用p({})或者p Hash.new来解决这个问题。 4.7 明明有pos=()这样的setter方法,可为什么pos=1时却没有任何反应呢?

请看下例。

class C attr_reader :pos def pos=(n) @pos = n * 3 end

def set pos = 1 #A行 end end

a = C.new a.set p a.pos #=> nil (预期值是 3)

本来指望最后一行能输出 3,但却是个 nil ,这是因为Ruby把A行的pos解释成局部变量了。若想调用pos=()的话,请这样self.pos = 1调用。 4.8 ‘\1’和’\1’有什么不同?

没有不同,二者完全一样。在单引号中,只有\‘、\和行尾的(取消换行)会得到特殊的解释,其他字符不变。 4.9 在p true or true and false中会显示true,但在a=true if true or true and false中却不会把true赋值给a。

第1个表达式会被解释成(p true) or true and false,其中的and/or是构成语句的要素,而并不是用来连接p的参数的操作符。

第2个表达是则会被解释成a=true if (true or true and false)。因为if的优先度低于and/or,且or与and的优先度相同,所以就会从左到右地完成解析。 4.10 为什么p(nil || “”)什么事儿都没有,可p(nil or “”)却会报错呢?

虽然||可以连接参数,但or就只能连接句子,所以如此。关于这点区别,您试一试下面的例子就明白了。

p nil || “” p nil or “”

  1. 方法

  2. 5.1 向对象发出消息之后,将按照什么顺序来搜索要执行的方法?

  3. 5.2 +和-是操作符吗?
  4. 5.3 Ruby中有函数吗?
  5. 5.4可以在外部使用对象的实例变量吗?
  6. 5.5 private和protected有什么不同?
  7. 5.6 能不能将实例变量变成public类型的变量?
  8. 5.7 怎样指定方法的可见性?
  9. 5.8 方法名可以用大写字母开头吗?
  10. 5.9 为什么使用super时会出现ArgumentError?
  11. 5.10 如何调用上2层的同名方法?
  12. 5.11 重定义内部函数时,如何调用原来的函数?
  13. 5.12 何谓破环性的方法?
  14. 5.13 那些情况下会产生副作用?
  15. 5.14 能让方法返回多个值吗?

5.1 向对象发出消息之后,将按照什么顺序来搜索要执行的方法?

将依次搜索特殊方法、本类中定义的方法和超类(包括Mix-in进来的模块。写成 类名.ancestors。)中定义的方法,并执行所找到的第一个方法。若没有找到方法时,将按照同样的顺序来搜索method_missing。

module Indexed def to_a[n] end end class String include Indexed end p String.ancestors # [String, Indexed, Enumerable, Comparable, Object, Kernel] p “abcde”.gsub!(/./, “\&\n”)[1]

遗憾的是上述代码返回的是10,而并非预期的”b\n”。这是因为系统在String类中搜索[],在遇到Indexed中定义的方法之前就已经完成了匹配,所以如此。若直接在Class String中重定义[]的话,就会如您所愿了。 5.2 +和-是操作符吗?

+和-等是方法调用,而并非操作符。因此可进行overload(重定义)。

class MyString < String def +(other) print super(other) end end

但以下内容及其组合(!=、!~)则是控制结构,不能进行重定义。

=, .., …, !, not, &&, and, |, or, ~, ::

重定义(或者定义)操作符时,应该使用形如+@或-@这样的方法名。

=是访问实例变量的方法,您可以在类定义中使用它来定义方法。另外,+或-等经过适当的定义之后,也可以进行形如+=这样的自赋值运算。

def attribute=(val) @attribute = val end

5.3 Ruby中有函数吗?

Ruby中看似函数的部分实际上都是些省略被调(self)的方法而已。例如

def writeln(str) print(str, “\n”) end

writeln(“Hello, World!”)

中看似函数的部分实际上是Object类中定义的方法,它会被发送到隐藏的被调self中。因此可以说Ruby是纯粹的面向对象语言。

对内部函数这种方法来说,不管self如何,它们总是返回相同的结果。因此没有必要计较被调的问题,可以将其看作函数。 5.4 可以在外部使用对象的实例变量吗?

不能直接使用。若想操作实例变量,必须事先在对象中定义操作实例变量的方法(accessor)。例如

class C def name @name end def name=(str) # name 后面不能有空格! @name = str end end

c = C.new c.name = ‘山田太郎’ p c.name #=> “山田太郎”

另外,您还可以使用Module#attr、attr_reader、 attr_writer、attr_accessor等来完成这种简单的方法定义。例如,您可以这样来重写上面的类定义。

class C attr_accessor :name end

若您不愿定义访问方法,却想使用实例变量时,可以使用Object#instance_eval。 5.5 private和protected有什么不同?

private意味着只能使用函数形式来调用该方法,而不能使用被调形式。所以,您只能在本类或其子类中调用private方法。

protected也是一样,只能用在本类及其子类中。但是您既可以使用函数形式又可以使用被调形式来调用它。

在封装方法时,该功能是必不可少。 5.6 能不能将实例变量变成public类型的变量?

无法让变量变成public类型的变量。在Ruby中访问实例变量时,需要使用访问方法。例如

class Foo def initialize(str) @name = str end

def name return @name end end

但是每次都这么写的话,未免有些繁琐。因此可以使用attr_reader、attr_writer、 attr_accessor等方法来完成这些简单的方法定义。

class Foo def initialize(str) @name = str end

attr_reader :name

其效果等同于下面的代码。

def name

return @name

end

end

foo = Foo.new(“Tom”) print foo.name, “\n” # Tom

您还可以使用attr_accessor来同时定义写入的方法。

class Foo def initialize(str) @name = str end

attr_accessor :name

其效果等同于下面的代码。

def name

return @name

end

def name=(str)

@name = str

end

end

foo = Foo.new(“Tom”) foo.name = “Jim” print foo.name, “\n” # Jim

若只想定义写入方法的话,可以使用attr_writer。 5.7 怎样指定方法的可见性?

首先 Ruby把那些只能以函数形式(省略被调的形式)来调用的方法叫做private方法。请注意,这里的private定义与C++以及Java中的定义不同。

若将方法设为private类型之后,就不能在其它的对象中调用该方法了。因此,若您只想在本类或其子类中调用某方法时, 就可以把它设为private类型。

您可以这样把方法设为private类型。

class Foo def test print “hello\n” end private :test end

foo = Foo.new foo.test

=> test.rb:9: private method `test’ called for #<Foo:0x400f3eec>(Foo)

您可以使用private_class_method将类方法变为private类型。

class Foo def Foo.test print “hello\n” end private_class_method :test end

Foo.test

=> test.rb:8: private method `test’ called for Foo(Class)

同理,您可以使用public、public_class_method将方法设为public类型。

在默认情况下,类中的方法都被定义成public类型(initialize除外),而顶层中的方法会被定义成private类型。 5.8 方法名可以用大写字母开头吗?

可以。但要注意:即使方法调用中不带参数,也不能省略方法名后的空括号。 5.9 为什么使用super时会出现ArgumentError?

在方法定义中调用super时,会把所有参数都传给上层方法,若参数个数不符合其要求,就会引发ArgumentError。因此,若参数个数不合时,应该自己指定参数然后再调用super。 5.10 如何调用上2层的同名方法?

super只能调用上1层的同名方法。若想调用2层以上的同名方法时,需要事先对该上层方法进行alias操作。 5.11 重定义内部函数时,如何调用原来的函数?

可以在方法定义中使用super。进行重定义之前,使用alias就可以保住原来的定义。也可以把它当作Kernel的特殊方法来进行调用。 5.12 何谓破环性的方法?

就是能修改对象内容的方法,常见于字符串、数组或哈希表中。一般是这样的:存在两个同名的方法,一个会拷贝原对象并返回副本;一个会直接修改原对象的内容,并返回修改后的对象。通常后者的方法名后面带有!,它就是破坏性的方法。但是有些不带!的方法也是具有破环性的,如String#concat等等。 5.13 那些情况下会产生副作用?

若在方法中对实参对象使用了破环性的方法的时候,就会产生副作用。

def foo(str) str.sub!(/foo/, “baz”) end

obj = “foo” foo(obj) print obj

=> “baz”

此时,参数对象的内容被修改。另一方面,如果在程序中确有必要的话,也会对某对象发送具有副作用的消息,那就另当别论了。 5.14 能让方法返回多个值吗?

在Ruby中确实只能指定一个方法返回值,但若使用数组的话,就可以返回多个值了。

return 1, 2, 3

上例中,传给return的列表会被当作数组处理。这与下面的代码可谓是异曲同工。

return [1, 2, 3]

另外,若使用多重赋值的话,则可以达到返回多个值的效果。例如

def foo return 20, 4, 17 end

a, b, c = foo print “a:”, a, “\n” #=> a:20 print “b:”, b, “\n” #=> b:4 print “c:”, c, “\n” #=> c:17

您也可以这样处理。