ruby参考手册VII
Ruby FAQ
- 一般的问题
- 变量、常数、参数
- 调用带块方法(迭代器)
- 句法
- 方法
- 类、模块
- 内部库
- 扩展库
- 尚未列出的功能
- 日语字符的处理
- 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.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/>%E3%80%82 1.6 请问有Ruby邮件列表吗?
现在有6个正式的Ruby邮件列表。
- ruby-list
- ruby-dev
- ruby-ext
- ruby-math
- ruby-talk
- ruby-core
详情请参考Ruby邮件列表。 1.7 怎么才能看到邮件列表中的老邮件?
另外,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.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)
现在只能在以下部分的尾部使用*
- 多重赋值的左边
- 多重赋值的右边
- 参数列表(定义方法时)
- 参数列表(调用方法时)
- 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.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.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.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 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
您也可以这样处理。