Ruby 新手须知事项(Things That Newcomers to Ruby Should Know)

作者: William Djaja Tjokroaminata
原文: http://www.glue.umd.edu/~billtj/ruby.html
译者: Johnson Qu http://jayesoui.blogspot.com

前言: 资源


* 主页
http://www.ruby-lang.org/en/
* 常见问题与解答
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/
* 非常有用的忠告
o Programming Ruby -- David Thomas 和 Andrew Hunt 的书,
"When Trouble Strikes" 一章, "But It Doesn’t Work" 一节
o The Ruby Way -- Hal Fulton 的书,
第一章: "Ruby In Review"


1.使用语法警告
用 "ruby -w"运行代码而不是简单的用"ruby"运行,以获得有用的警告. 如果不直接援用"ruby", 你可以设置环境变量 RUBYOPT 为 ‘w’:
 
Win32 环境下:
C:\> set RUBYOPT=w
或者在Scite编辑器中按 F5 (来执行)会给你警告,F4会定位到问题行.

Unix 环境下:
sh# export RUBYOPT="w"
或:
csh# setenv RUBYOPT "w"


2.交互式的壳(shell)
Ruby有个交互式的壳; 试着援用命令 "irb" 而非 "ruby". "irb" 被很好的用来试验语言和类;你可以把东西在这个环境里弄清楚,然后再写到你的程序里.

C:\> irb
irb(main):001:0> def hello_world
irb(main):002:1> puts "Hello, world!"
irb(main):003:1> end
=> nil
irb(main):004:0> hello_world
Hello, world!
=> nil
irb(main):005:0>


3.屏幕文档
要便利的屏显文档, 考虑使用 (如果必要的话并且安装) "ri",可以在下边找到:
http://www.pragmaticprogrammer.com/ruby/downloads/ri.html
例如,查看File类的方法,运行"ri File". 阅读它的open方法, 输入 "ri File.open".

4. Class#method 符号
文档里的"Klass#method"符号仅用来表示Klass类的一个对象的"实例方法";它根本不是Ruby的语法. 另一方面, 文档里的"类方法",通常表示为"Klass.method" (这个是有效的Ruby语法).

5. 从字串中得到字符
String#[Fixnum] 方法并不返回在Fixnum位置的"字符",而是返回这个位置ASCII字符的编码(然而,这将来可能改变). 目前,要得到字符本身,使用 String#[Fixnum,1] 来替代.
更进一步,如下有额外的 ASCII 码转换方法:

* 用 Integer#chr 来从ASCII码转换为字符
65.chr # -> "A"
* 用 ?chr 来从字符转换到ASCII码
?A # -> 65

例如,利用这些性质,可以"aString[-1, 1]" 或者 "aString[-1].chr"这么写来得到字串的最后一个字符.

6. 数组和散列(杂凑hash)的默认值
注意:
# 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. 易变的(mutable)散列的键
当使用"易变的"对象作为散列的键时要小心.为得到期望的结果,要在访问散列元素前调用 Hash#rehash. 例如:

s = "mutable"
arr = [s]
hsh = { arr => "object" }
s.upcase!
p hsh[arr] # -> nil (可能不是所期望的)
hsh.rehash
p hsh[arr] # -> "object"


8. 从文件中读取数字
从文件读取数据并把他们放到变量里以后,数据类型实际上是字串.要转换他们为数字,用 "to_i" 或 "to_f" 方法. 举个例子,如果你使用"+"操作符来加这些"数字"而没有调用转换方法, 你将只是简单的把字串连接起来.
另一个选择是使用(C风格的)"scanf" (http://www.rubyhacker.com/code/scanf).

9. 前/后 增/减 操作符
Ruby没有前/后,增/减操作符.例如, x++ 或 x-- 会解析出错. 更重要的是, ++x 或 --x 会什么也不做! 实际上,他们表现为多个一元前缀操作符: -x == ---x == -----x == ...要增加数字,直接写 x += 1.
Ruby语言作者对这个语法设计的一个解释可以在下边找到:
http://www.ruby-talk.org/2710

10. 块中的语汇范围(lexical scoping)
当心本地变量和块的本地变量间的语汇范围(lexical scoping)的相互影响. 如果一个本地变量已经在块前被定义, 那么块会使用(并且很可能修改)本地变量; 这样块并不引入一个新的范围. 例如:

(0..2).each do |i|
puts "块内: i = #{i}"
end
puts "块外: i = #{i}" # -> 未定义的'i'

另一方面:
i = 0
(0..2).each do |i|
puts "块内: i = #{i}"
end
puts "块外: i = #{i}" # -> '块外: i = 2'

还有:
j = 0
(0..2).each do |i|
j = i
end
puts "块外: j = #{j}" # -> '块外: 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 语句中:

case obj
when obj_1
...
when obj_k
...

涉及到"==="方法,而不是"=="方法.同时,顺序是"obj_k === obj"而不是"obj === obj_k".
用这种顺序是因为case语句可以以更灵活的方式"搭配" 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对象的"e6"方法(是个Fixnum).你应该写成"1.0e6".
然而,注意虽然"点"是最强的操作符,它的优先级顾及到方法名可能会随不同的Ruby版本而不同. 至少在Ruby 1.6.7, "puts (1..3).length" 会给你个语法错误;你应该写"puts((1..3).length)"来代替.


15.区间对象(Range object)
"0..k" 表示一个区间对象(Range object), 而 "[0..k]"表示一个只有单个区间类型的元素的数组. 例如, 如果

[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没有"元组(Tuple)"类型的对象(也就是永远不变的数组).还有通常用圆括号括起区间对象来达到优先组合的目的(因为在上边的例子中"点"比"点点"更强).


16. 布尔值
Ruby 中, 只有 false 和 nil 在布尔逻辑表达式中被认为是假. 特别的, 0 (零), "" 或者 '' (空字串), [] (空数组), 和 {} (空散列) 都被认为是真.

17.变量,引用和对象
Ruby变量保持对对象的引用并且 = 操作符拷贝引用.同时,例如a += b的自赋值实际上被翻译为a = a + b. 因此意识到在某个操作中你事实上是创建了新的对象还是修改了一个现存的对象是明智的.
例如, string << "another" 比 string += "another" 更安全(不创建额外对象), 所以如果它存在的话你使用任何类定义的(class-defined)更新方法(update-method) 应该更好(如果那是真是你的意图). 然而,也要注意关连同一个对象的所有其他变量上的"副作用":

a = 'aString'
c = a
a += ' modified using +='
puts c # -> "aString"

a = 'aString'
c = a
a << ' modified using <<'
puts c # -> "aString modified using <<"

18. 深拷贝(deep copy)
Ruby 中没有内建的标准深拷贝. 达到类似效果的一个办法是用 serialization/marshalling. 因为在Ruby里一切都是引用, 当你想 "拷贝"(例如靠使用dup或clone方法)对象 时要小心, 特别是对于包含其他对象的对象(例如数组和散列)并且当包含不止一层时.


19. 类变量
一个类变量 is in general per-hierarchy, not per-class (就是说,类变量被父类和所有它的子类"共享",外加被所有那个类的实例共享). 有个微妙的例外是如果一个子类在它的父类之前创建了一个类变量. 例如,当一个父类首先创建了一个类变量:

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 # 注意
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 事实上被Base和Derived类"共享". 然而,现在看看当一个子类首先创建变量时会发生什么:

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 # 改变了
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

21. 后记:知道有好处的东西
Ruby 中"自赋值操作符"不止 +=, -=, *=, /=, %=. 特别是,像"||="这样的赋值符也是存在(但是目前不能用在没有先定义的类变量上;这点以后可能会改变).完整赋值符请看"Programming Ruby"这本书的表格18.4..

想要有许多算法和示例代码的"菜谱"可以考虑 PLEAC-Ruby:
http://pleac.sourceforge.net/pleac_ruby/t1.html

对于扩展数字运算考虑 Numerical Ruby:
http://www.ir.isas.ac.jp/~masa/ruby/index-e.html

对于消耗大量的内存和CPU时间的(数字)阵列,考虑 NArray ,它是 Numerical Ruby的一部分:
http://www.ir.isas.ac.jp/~masa/ruby/na/SPEC.en

要想加速你的部分Ruby代码可以把它们用C来写,考虑Inline:
http://sourceforge.net/projects/rubyinline/

想要把Ruby翻译为C考虑 rb2c:
http://easter.kuee.kyoto-u.ac.jp/~hiwada/ruby/rb2c/

对Ruby和C/C++的整合,考虑 SWIG:
http://www.swig.org/

Ruby和Java的整合,考虑 JRuby:
http://jruby.sourceforge.net/

Ruby和Delphi的整合,考虑 Apollo:
http://www.users.yun.co.jp/~moriq/apollo/index-en.html

对于在Ruby中嵌入Python,考虑 Ruby/Python:
http://www.ruby-lang.org/en/raa-list.rhtml?name=Ruby%2FPython

在Ruby中嵌入Lua, 考虑 Ruby-Lua:
http://ruby-lua.unolotiene.com/ruby-lua.whtm

创建独立的(Windows)可执行文件,考虑 exerb:
http://exerb.sourceforge.jp/index.en.html

要想操作原始的位(raw bits),而不使用Fixnums,考虑 BitVector:
http://www.ce.chalmers.se/~feldt/ruby/extensions/bitvector/

For comments on this list, you may e-mail me directly at: billtj@glue.umd.edu

    
Copyright notice
Copyright © 2002-2006 William Djaja Tjokroaminata. All rights reserved.
本中文翻译版 Creative Commons 署名

0 评论: