浏览 2831 次
锁定老帖子 主题:ruby参考手册VII
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-01-04
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 您也可以这样处理。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |