当你第一眼看见Ruby 程式码,你一定会想起你熟悉的程式语言。这正是本文件的目的。Ruby 有许多语法和Perl、Python 和Java (以及其他程式语言) 类似,所以如果你已经熟悉这些程式语言,那么学习Ruby 易如反掌。
这份文件包括两大部份。这一部分的用意是整理从X语言到Ruby的 ??重点。第二部分则从Ruby的 ??重要功能及特色着手,与其他程式语言来做比较。
重点整理:从程式语言X到Ruby
重要的语言特色及一些诀窍
这里是你学习Ruby 的一些重点及提示。
迭代(Iteration)
Ruby有两个常用的特色你可能没见过,那就是“程式区块(blocks)”和迭代子(iterators)”。不像使用索引的回圈(例如C, C++和pre-1.5 Java) ,或是回圈控制结构(例如Perl的for (@a) {...},或是Python的for i in aList: ... )。在Ruby里你会常常看到:
some_list . each do | this_item |
#我们在程式区块中
#处理this_item
end
关于更多each
的资讯(以及collect
, find
, inject
, sort
等等),请参考ri Enumerable
(和ri Enumerable# func_name ).
一切东西都有值
表达式(expression)和叙述(statement)没有差别,都会有回传值,即使那个值是nil。例如下述用法:
x = 10
y = 11
z = if x < y
true
else
false
end
z # => true
Symbols 不是轻量化的字串
许多Ruby 新手会搞不清楚什么是Symbols(符号) 可以做什么用。
Symbols就如同一个识别符号。一个symbol就代表它是”谁”了,而不是代表它是”什么”。打开irb
来看一看它们的区别:
irb ( main ): 001 : 0 > :george . object_id == :george . object_id
=> true
irb ( main ): 002 : 0 > " george ". object_id == " george ". object_id
=> false
irb ( main ) : 003 : 0 >
object_id
方法会回传物件的识别编号。如果有两个物件有相同的 object_id
表示它们其实是同一个(指向同一个记忆体位置)。
如你所见,使用过Symbols之后,任何相同名字的Symbol都是指记忆体里的同一个物件。对任何相同名字的Symbols,它们的object_id
都一样。
让我们来看看字串“george”,它们的object_id
并不相同。这表示它们在记忆体里面是不同的物件。每次你建立一个新的字串,Ruby就会分配新的记忆体空间给它。
如果你不清楚何时使用Symbol 何时用字串(String),想想看用途究竟是物件的识别(例如一个杂凑Hash 的key),还是物件内容(比如这个例子的“george”)。
所有东西都是物件
“所有东西都是物件” 并不是夸大,甚至是类别跟整数也是物件,你可以与其他物件一样操作它们:
#这是等价的程式:
# class MyClass
# attr_accessor :instance_var
# end
MyClass = Class . new do
attr_accessor :instance_var
end
译注:在Ruby中任何类别都是由Class
类别所实例(new)出来的物件。
可变的常数
常数(Constant)并不真的无法改变。如果你修改了一个已经有值的常数,你会得到一个警告讯息,但程式不会终止。当然这不表示你”应该”修改常数的值。
命名惯例
Ruby规定了一些命名惯例。变数的识别名称,大写字母开头的是常数、 ??钱号($)开头的是全域变数、@
开头是实例变数(instance variable)、@@
开头则是类别变数。
方法名称可以允许大写字母开头,虽然可能造成一些混淆,例如:
Constant = 10
def Constant
11
end
这里的Constant
是10,但是Constant()
却是11。
虚拟关键字参数
Ruby 不像Python 有关键字参数(keyword parameters)功能,但是可以用symbols 和杂凑(hash) 来替代。Ruby on Rails 和非常多的函式库都使用了这个方法,例如:
def some_keyword_params ( params )
params
end
some_keyword_params ( :param_one => 10 , :param_two => 42 )
# => {:param_one=>10, :param_two=>42}
一切为true
在Ruby里,除了nil和false之外的所有东西,都可以当做true值。在C, Python和其他语言中,0和一些其他值,例如空列表,会被当做false。例如我们看看以下的Python程式(其他语言亦同):
# in Python
if 0 :
print " 0 is true "
else :
print " 0 is false "
这会输出“0 is false”。而在Ruby 里:
# in Ruby
if 0
puts " 0 is true "
else
puts " 0 is false "
end
这会输出“0 is true”。
存取修饰词会作用到底
在下面的Ruby 程式中,
class MyClass
private
def a_method ; true ; end
def another_method ; false ; end
end
你可能会认为another_method
是public的,但不是这样。这个'private'存取修饰到作用域(scope)结束,或是直到另一个存取修饰词开始作用。方法预设都是public的:
class MyClass
#这个a_method是public的
def a_method ; true ; end
private
#这个another_method是private的
def another_method ; false ; end
end
public
, private
和protected
其实也是一种方法,所以可以接受参数。如果你传入一个Symbol,那个该Symbol代表的方法就会改变存取权限。
方法存取权限
在Java里,public
表示方法可以被任何人呼叫。protected
表示只有这个类别的实例、衍生类别的实例,以及相同package类别的实例可以呼叫,而private
表示除了这个类别的实例之外,其他都不行呼叫。
在Ruby中,public
还是一样是公开的意思,其他则有一点差异。private
表示只有不指定接受者(receiver)时才可以呼叫,也就是只有self可以当成private方法的接受者。
protected
也有点不同。一个protected方法除了可以被一个类别或衍生类别的实例呼叫,也可以让另一个相同类别的实例来当做接受者。
来看看Ruby FAQ的例子:
$ irb
irb ( main ): 001 : 0 > class Test
irb ( main ): 002 : 1 > #预设是public的
irb ( main ): 003 : 1 * def func
irb ( main ): 004 : 2 > 99
irb ( main ): 005 : 2 > end
irb ( main ): 006 : 1 >
irb ( main ): 007 : 1 * def == ( other )
irb ( main ): 008 : 2 > func == other . func
irb ( main ): 009 : 2 > end
irb ( main ): 010 : 1 > end
=> nil
irb ( main ): 011 : 0 >
irb ( main ): 012 : 0 * t1 = Test . new
=> #<Test: 0x34ab50>
irb ( main ): 013 : 0 > t2 = Test . new
=> #<Test:0x342784>
irb ( main ): 014 : 0 > t1 == t2
=> true
irb ( main ): 015 : 0 > #来让`func`变成protected,一样没问题
irb ( main ): 016 : 0 * #因为protected允许其他相同类别的实例呼叫
irb ( main ): 017 : 0 * class Test
irb ( main ): 018 : 1 > protected :func
irb ( main ): 019 : 1 > end
=> Test
irb ( main ): 020 : 0 > t1 == t2
=> true
irb ( main ): 021 : 0 > #来让`func`变成private
irb ( main ): 022 : 0 * class Test
irb ( main ): 023 : 1 > private :func
irb ( main ): 024 : 1 > end
=> Test
irb ( main ): 025 : 0 > t1 == t2
NoMethodError : private method ` func ' called for #<Test:0x342784>
from (irb):8:in `== '
from ( irb ): 25
from : 0
irb ( main ): 026 : 0 >
类别是开放的
Ruby的 ??类别是开放的,你可以随时打开它新增一点程式或是修改。即使是核心类别如Fixnum
或是Object
(这是所有类别的父类别)都一样。Ruby on Rails甚至定义了一堆时间方法到Fixnum
去,例如:
class Fixnum
def hours
self * 3600 #一小时有多少秒
end
alias hour hours
end
#从一月一号00:00往后数14个小时
# (你终于醒了吧;)
Time . mktime ( 2006 , 01 , 01 ) + 14 . hours # => Sun Jan 01 14:00:00
有趣的方法名称
在Ruby里,方法名称允许用问号或惊叹号结尾。惯例上,用来回答是非题的方法会用问号结尾(例如Array#empty?会回传true如果方法接收者是空的)。有潜在“危险” (表示有某种副作用,会修改self或参数值。例如exit!
等)的方法会用惊叹号结尾。
但是这不表示所有会修改参数的方法一定有惊叹号结尾,例如Array#replace就会替换内容成别的阵列,毕竟replace的意思就是要修改替换自己。
单件方法
单件方法(Singleton methods)是个别物件才有的方法。它们只存在于你要定义的物件之中。
class Car
def inspect
" Cheap car "
end
end
porsche = Car . new
porsche . inspect # => Cheap car
def porsche.inspect
" Expensive car "
end
porsche . inspect # => Expensive car
#其他物件就不受影响
other_car = Car . new
other_car . inspect # => Cheap car
Missing 方法
当你呼叫一个不存在的方法,Ruby仍然有办法处理。它会改呼叫method_missing
这个方法,并把这个不存在的方法名称传进去当做参数。method_missing预设会丢出一个NameError例外,但是你可以根据你的需求重新定义过,也有许多函式库这么做。这是一个例子:
# id是被呼叫方法的名字,而*符号会收集
#所有传进来的参数变成一个叫做'arguments'的阵列
def method_missing ( id , * arguments )
puts " Method #{id} was called, but not found. It has " +
" these arguments: #{arguments.join(", ")} "
end
__ :a , :b , 10
# => Method __ was called, but not found. It has these
# arguments: a, b , 10
以上程式会输出呼叫的细节,但你可以随意定义这个讯息。
传递讯息,不是呼叫函数
一个方法呼叫(method call)其实就是送一个讯息(message)给一个物件:
#这个
1 + 2
#等同于...
1 .+( 2 )
#也等同于:
1 . send " + ", 2
Blocks 也算是物件
程式区块Blocks (或叫做closures)被广泛应用在标准函式库。要执行一个程式区块,可以用 yield
,或是透过一个特别的参数让它变成Proc
,例如:
def block ( & the_block )
#在这里面,the_block是被传进来的程式区块
the_block # return the block
end
adder = block { | a , b | a + ??b }
# adder是一个Proc物件
adder . class # = > Proc
你也可以透过Proc.new 或lambda 在方法外建立程式区块。
同样的,方法也可以当做物件:
method ( :puts ). call " puts is an object! "
# => puts is an object!
操作符只是语法包装
大部分的Ruby 操作符(operators)只是一种方法呼叫的语法包装(syntactic sugar),加上一些优先权规则。你要的话,举例来说,我们可以覆写掉Fixnum 的+ 方法:
class Fixnum
#可以这么做,但请不要这么改
def + ( other )
self - other
end
end
你不需要C++的operator+
等等。
甚至有如阵列存取的[]
和[]=
可以定义。要定义一元的+ and – (想想看+1跟-2),你必须分别定义+@
和-@
方法。
以下的操作符则不是语法包装。它们不是方法,不能被覆写定义:
=, .., ..., !, not, &&, and, ||, or, !=, !~, ::
此外+=, *=等只是var = var + other_var
跟var = var * other_var
等的缩写,因此也不能被覆写定义。
更多资料
如果你需要更多Ruby知识,请参考文件。