作者:William Djaja Tjokroaminata
目录
·资源:
主页:http://www.ruby-lang.org/en/
FAQ:http://www.rubycentral.com/faq/(旧版)
http://www.rubygarden.org/iowa/faqtotum(最新版)
缺陷:http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb?cmd=view;name=pitfall
网上教程/文档/书籍:http://www.rubycentral.com/book/
有用的信息:
◎David Thomas和Andrew Hunt所著《Programming Ruby》一书,“When Trouble Strikes”一章中,“But It Doesn't Work”节
◎Hal Fulton所著《The Ruby Way》,第一章“Ruby In Review”
1、使用“ruby -w”代替简单的“ruby”以获得有用的警告信息。如果你不是直接调用“ruby”,那么你可以将环境变量RUBYOPT设置为“w”:
·Win32:
C:\> set RUBYOPT=w
或者
在Scite editor中按F5(执行)就可以得到警告信息(F4可以定位有问题的行)。
·UNIX:
sh# export RUBYOPT="w"
或者
csh# setenv RUBYOPT "w"
2、Ruby有一个交互式命令解释器,你可以键入“irb”来调用。“irb”是进行语言和类试验的最好工具,你可以在“irb”中验证你的代码,然后再把它添加到你的程序中。
3、要获得方便的联机帮助,请使用(如果没有安装请先安装)“ri”(http://www.pragmaticprogrammer.com/ruby/downloads/ri.html)。
例如,要查看File类的方法,键入“ri File”。要获得它的open方法的更多内容,键入“ri File.open”。
4、文档中“Klass#method”表示一个Klass类对象所具有的一个“实例方法”,它并不是Ruby的语法。另外,在文档中一个“类方法”的表示方法为“Klass.method”(这是一个正确的Ruby语法)。
5、String#[Fixnum]方法并不返回Fixnum位置上的“字符”(就是长度为1的字符串),而是返回字符对应的ASCII码(在Ruby未来的版本中可能会改变)。眼下,为了获得字符本身,要使用String#[Fixnum, 1]。
此外,还有一些其它的ASCII转换方法可供使用:
·Integer#chr将ASCII转换为对应的字符
65.chr # -> "A"
·?chr将字符转换为ASCII码
?A # -> 65
举个例子,如果要用以上方法获取字符串中的最后一个字符,可以这样“aString[-1, 1]”,或者“aString[-1].chr”。
6、Array.new(2, Hash.new) # -> [{}, {}]
但是数组中的两个元素是同一个对象,而不是独立的哈希表。要创建包含(独立的)哈希表的数组,请使用“map”或“collect”方法:
arr = (1..2).map {Hash.new}
同样,要创建一个数组哈希表,下面的代码可能也达不到目的:
hsh = Hash.new([])
while line = gets
>if line =~/(\S+)\s+(\S+)/
hsh[$1] << $2
end
end
puts hsh.length # -> 0
另一个正确且简洁的方法是“(hash[key] ||= []) <<value”,比如
hsh = Hash.new
while line = gets
if line =~/(\S+)\s+(\S+)/
(hsh[$1] ||= []) << $2
end
end
7、使用“可变”对象作为哈希表的键时要小心。要获得期望的结果,在访问哈希表元素前记得调用Hash#rehash。例如:
s = "mutable"
arr = [s]
hsh = { arr => "object" }
s.upcase!
p hsh[arr] # -> nil (也许不是期望获得的结果)
hsh.rehash
p hsh[arr] # -> "object"
8、当从文件中读取数据到变量时,这些变量的类型是String。要把它们转换为数字,使用“to_i”或者“to_f”方法。假如——举个例子——你没有调用这些转换方法就直接使用“+”运算符对两个“数字”相加,那么你只是在连接字符串。
另一个可选的方法是使用“scanf”(http://www.rubyhacker.com/code/scanf)。
9、Ruby没有诸如x++或者x--这样的前缀和后缀自加/自减运算符,类似的语句将导致解析错误。更要命的是,++x或者--x这样的语句将不会有任何作用!事实上,它们的行为和多个一元前缀运算符相同:-x == ---x == -----x == ……要自加一个数,这样:x += 1。
(对于为什么这样设计,你可以在http://www.ruby-talk.org/2710找到一个作者本人的解释。)
10、注意区分局部变量和块局部变量在作用域上的区别。如果在一个语句块前局部变量已经被定义,那么语句块将直接使用这个变量(而且很有可能改变这个变量),在这种情况下语句块并不会引入新的作用域。例如:
(0..2).each do |i|
puts "inside block: i = #{i}"
end
puts "outside block: i = #{i}" # -> 未定义'i'
另一方面,
i = 0
(0..2).each do |i|
puts "inside block: i = #{i}"
end
puts "outside block: i = #{i}" # -> 'outside block: i = 2'
和
j = 0
(0..2).each do |i|
j = i
end
puts "outside block: j = #{j}" # -> 'outside block: j = 2'
11、在Ruby中有两套逻辑运算符:[!,&&,||]和[not,and,or]。[!,&&,||]的优先级高于赋值运算符(=,%=,~=,/=等等),而[not,and,or]的优先级则低于赋值运算符。另外要注意,&&的优先级高于||,而and的优先级则与or相同。举个例子:
a = 'test'
b = nil
both = a && b # both == nil
both = a and b # both == 'test'
both = (a and b) # both == nil
(在http://www.rubygarden.org/iowa/faqtotum/abN18mrYFE49E/c/1.13.3.3.5你可以找到这个语言设计的原因和一些例子。)
12、在case语句
case obj
when obj_1
....
when obj_k
....
中,调用的是“===”方法而不是“==”方法。另外,运算的顺序是“obj_k === obj”而不是“obj === obj_k”。
以这样的顺序进行运算可以使Ruby更灵活的匹配obj。有意思的是当obj_k分别为Module/Class、Regexp和Range时:
·Module/Class类定义了“===”方法,用于测试obj是否是此模块/类的实例或者其派生类的实例(“obj#kind_of?obj_k”)。
·Regexp类定义了“===”方法,用于判断obj是否匹配此模式(“obj =~ obj_k”)。
·Range类定义了“===”方法,用于判断obj是否是此范围中的一个元素(“obj_k.include?obj”)。
13、在调用方法时,不要在括号“(”前加空格。另外,如果你将$VERBOSE设为true,那么Ruby将因此产生一个警告。
14、用于函数调用的“点”运算符是最强的运算符。因此,在Ruby中表示浮点数的小数点后必须是数字,而在其它许多语言中并没有这个要求。例如,“1.e6”将会调用对象1(一个Fixnum对象)的方法“e6”。你必须使用“1.0e6”。
不过注意,尽管“点”运算符是最强的运算符,但是与方法名连用时,不同版本的Ruby表现是不一样的。至少在Ruby1.6.7中,“puts (1..3).length”将会产生一个语法错误,此时应该使用“puts((1..3).length)”。
15、“0..k”代表一个Range对象,而“[0..k]”代表一个数组,这个数组只有一个Range类型的元素。
举个例子,如果
[0..2].each do |i|
puts "i = #{i}"
end
没有给出你想要的结果,那么你也许应该试试
(0..2).each do |i|
puts "i = #{i}"
end
或者
0.upto(2) do |i|
puts "i = #{i}"
end
注意Ruby没有元组(不可变的数组)类型的对象,而括号也经常放在Range对象周围以确保优先级正确(例如上例中“点”运算符强于“点点”运算符)。
16、在Ruby中,只有false和nil在布尔运算中被认为是false。特别是0(零),“”和‘’(空字符串),[](空数组),{}(空哈希表)都被认为是true。
17、Ruby中的变量只保存对象的引用,因此使用=运算符时仅复制引用本身。另外,诸如a += b的自赋值也被转换为a = a + b的形式。因此你最好知道某个操作究竟是创建了一个新的对象还是改变了一个已经存在的对象。
例如,string << "another"比string += "another"快(没有创建额外的对象),所以你理所当然应该使用某个类已经定义的更新函数(如果你真的想这么做)。不过,小心“边界效应”,它会影响所有其它引用同一个对象的变量:
a = 'aString'
c = a
a += ' modified using +='
puts c # -> "aString"
a = 'aString'
c = a
a << ' modified using <<'
puts c # -> "aString modified using <<"
18、在Ruby中没有标准的内建深度拷贝方法。要获得相同的效果可以使用序列化/排列(serialization/marshalling)技术。因为在Ruby中所有的东西都是引用,所以“拷贝”对象(比如通过dup或clone方法)时要小心,尤其是拷贝那些包含其它对象的对象(比如数组和哈希表)且包含的深度又超过一层时。
19、一般情况下,一个类变量是属于整个继承链而不仅仅是类本身的(比如,一个类变量由父类及其所有派生类所“共享”的)。令人迷惑的是,如果一个子类在其父类之前创建了类变量,那么情况就不同了。例如,如果一个父类首先创建了一个类变量:
class Base
def initialize
@@var = 'base'
end
def base_set_var
@@var = 'base'
end
def base_print_var
puts @@var
end
end
class Derived < Base
def initialize
super
@@var = 'derived'
end # notice
def derived_set_var
@@var = 'derived'
end
def derived_print_var
puts @@var
end
end
d = Derived.new
d.base_set_var
d.derived_print_var # -> 'base'
d.base_print_var # -> 'base'
d.derived_set_var
d.derived_print_var # -> 'derived'
d.base_print_var # -> 'derived'
在上面这段代码中,类变量@@var实际上是被基类和派生类所“共享”的。现在再来看看如果一个子类先创建类变量会发生什么:
class Base
def initialize
@@var = 'base'
end
def base_set_var
@@var = 'base'
end
def base_print_var
puts @@var
end
end
class Derived < Base
def initialize
@@var = 'derived'
super
end # changed
def derived_set_var
@@var = 'derived'
end
def derived_print_var
puts @@var
end
end
d = Derived.new
d.base_set_var
d.derived_print_var # -> 'derived'
d.base_print_var # -> 'base'
d.derived_set_var
d.derived_print_var # -> 'derived'
d.base_print_var # -> 'base'
在这种情况下,父类和子类就具有了两个相互独立但又具有相同名字的类变量。
20、反斜线的替换是比较奇特的。例如:
str = 'a\b\c' # -> a\b\c
puts str.gsub(/\\/,'\\\\') # -> a\b\c
puts str.gsub(/\\/,'\\\\\\') # -> a\\b\\c
puts str.gsub(/\\/,'\\\\\\\\') # -> a\\b\\c
puts str.gsub(/\\/) { '\\\\' } # -> a\\b\\c
puts str.gsub(/\\/, '\&\&') # ->a\\b\\c
·你最好知道
a.在Ruby中,“自赋值运算符”不止是“+=,-=,*=,/=,%=”这几个。尤其是像“||=”这样的运算符也是可用的(目前这些运算符还不能作用于未定义的类变量,不过在将来版本的Ruby中可能会有所改变)。完整的列表请参考《Programming Ruby》一书中的表18.4。
b.在“PLEAC-Ruby”(http://pleac.sourceforge.net/pleac_ruby/t1.html)上有一个包含很多算法和例程的手册。
c.在“Numerical Ruby”(http://www.ir.isas.ac.jp/~masa/ruby/index-e.html)上有更多关于数字计算的信息。
d.在“Numerical Ruby”(http://www.ir.isas.ac.jp/~masa/ruby/na/SPEC.en)上还有一个“NArray”,它比Ruby本身的(数字)数组占用更少的内存,运行速度也较快。
e.要在Ruby中嵌入C语言编写的代码以提高Ruby代码的运行速度,请参考“Inline”(http://sourceforge.net/projects/rubyinline/)。
f.要将Ruby程序代码转换为C程序代码,请参考“rb2c”(http://easter.kuee.kyoto-u.ac.jp/~hiwada/ruby/rb2c/)。
g.要集成Ruby和C/C++,请参考“SWIG”(http://www.swig.org/)。
h.要集成Ruby和Java,请参考“JRuby”(http://jruby.sourceforge.net/)。
i.要集成Ruby和Delphi,请参考“Apollo”(http://www.users.yun.co.jp/~moriq/apollo/index-en.html)。
j.要在Ruby中嵌入Python,请参考“Ruby/Python”(http://www.ruby-lang.org/en/raa-list.rhtml?name=Ruby%2FPython)。
k.要在Ruby中嵌入Lua,请参考“Ruby-Lua”(http://ruby-lua.unolotiene.com/ruby-lua.whtm)。
l.要通过Ruby创建Windows下的可执行程序,请参考“exerb”(http://exerb.sourceforge.jp/index.en.html)。
m.要操作原始的二进制位而不是使用Fixnum,请参考“BitVector”(http://www.ce.chalmers.se/~feldt/ruby/extensions/bitvector/)。
·任何关于本文档的问题请致信mailto:billtj@glue.umd.edu。
·任何关于本文档简体中文版的问题请致信mailto:leasun@yeah.net。
最后更新:2002年11月25日
本文档英文版可在http://www.glue.umd.edu/~billtj/ruby.html找到。