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.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][英译稿](June 11, 1999)中讲过取名的经过。

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

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

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

松本先生曾经在[ruby-talk:00382][英译稿](June 4, 1999)中介绍过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/>找到最新版的Ruby。

镜像站点列表如下

* <URL:ftp://ftp.TokyoNet.AD.JP/pub/misc/ruby/>
* <URL:ftp://ftp.iij.ad.jp/pub/lang/ruby/>
* <URL:ftp://blade.nagaokaut.ac.jp/pub/lang/ruby/>
* <URL:ftp://ftp.krnet.ne.jp/pub/ruby/>
* <URL:ftp://mirror.nucba.ac.jp/mirror/ruby/>
* <URL:http://mirror.nucba.ac.jp/mirror/ruby/>

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

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

Ruby的官方网站是<URL:http://www.ruby-lang.org/>。
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>和<URL:http://ruby.freak.ne.jp/>里面有搜索邮件用的表单。

另外,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。若您能提供这些信息就更好了。


2. 变量、常数、参数

* 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 常数不能被修改吗?

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。

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


3. 带块的方法调用

* 3.1 什么是"带块的方法调用"?
* 3.2 怎么将块传递给带块方法?
* 3.3 如何在主调方法中使用块?
* 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时, 一般都假定在方法调用时会传过来一个块。


4. 句法

* 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 "")却会报错呢?

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


5. 方法

* 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 能让方法返回多个值吗?

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

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

module Indexed
def [](n)
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

您也可以这样处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值