Thursday, October 09, 2008

(8)Ruby中的类和方法

就是一些列方法的集合,这些方法可以操作类的状态,中的变量维护了类的状态。由@开头的变量是某个具体类的专有变量,下面的例子演示了如何定义类、方法以及如何使用它们:


Created with colorer-take5 library. Type 'ruby'

#
# 这个类呈现了一系列数字操作,使用了3个参数,from ,to ,by 。变量x遵从如下规则
#
# from <= x <= to
# x = from + n*by, n是一个整数
#
class Sequence
# 这个是enumerable类; 它定义了一个each迭代.
include Enumerable # 在这个模块中导入Enumeable的方法

# initialize是一个特殊方法,它在类被创建的时候自动调用
def initialize(from, to, by)
# 把参数值存储在局部变量中以便后边使用
@from, @to, @by = from, to, by # 注意这里使用了平行赋值和@前缀
end

# 定义Enumerable模块所需的迭代方法
def each
x = @from # 迭代开始,将x值赋值为@from
while x <= @to # 开始循环直到x小于等于@to的值
yield x # 将x之传递给相应迭代的 块
x += @by # 用@by值递增x的值
end
end

# 定义lenght方法返回数组的长度
def length
return 0 if @from > @to # 如果from大于to说明长度为0
Integer((@to-@from)/@by) + 1 # 计算并返回数组长度
end

# 给length定义一个别名方法.
# Ruby中一个方法通常有多个名称
alias size length # 现在size方法与length是相同的。

#重载数组下标访问的操作符
def[](index)
return nil if index < 0 # 如果是负数的下标就返回nil
v = @from + index*@by # 计算返回值
if v <= @to # 判断它是否为数组成员
v # 如果是就返回
else # 否则
nil # 返回nil
end
end

# 重载算数操作符,返回一个新的序列
def *(factor)
Sequence.new(@from*factor, @to*factor, @by*factor)
end

def +(offset)
Sequence.new(@from+offset, @to+offset, @by)
end
end

下面演示了如何使用这个类:
s = Sequence.new(1, 10, 2)  # 定义1到10,步长为2
s.each {|x| print x } # 打印 "13579"
print s[s.size-1] # 打印 9
t = (s+1)*2 # 从4到22,步长为4

实际上我们这个类中最关键的部分是迭代方法,实际上如果你只是对迭代方法感兴趣你不必一定要定义一个类,你可以直接在脚本文件中写这个方法,这样它就是一个全局的方法,当然定义太多的全局方法不是个好主意,我们可以把这个方法放到一个模块中module:
module Sequences                   # 定义一个模块
def self.fromtoby(from, to, by) # 在模块中定义一个方法
x = from
while x <= to
yield x
x += by
end
end
end

通过这种方式定义的方法,我们可以这样调用:
Sequences.fromtoby(1, 10, 2) {|x| print x }  # 打印"13579"

我们使用了另一种方式来定义迭代方法,但是看起来方法名太长了,而且调用它的时候也比较麻烦,有没有简便的办法呢?实际上我们只是想在Range类型中迭代,并且定义一个方法可以自定义步长,Ruby中一个神奇的特性是它的类是“开放”的,甚至是它的核心类,你可以在任何时候向其中新增方法,所以让我们来给Range定义一个新的方法:

Created with colorer-take5 library. Type 'ruby'

class Range # 打开一个现存的类,以便给他增加新的方法。
def by(step) # 定义迭代方法的名称为by
x = self.begin # 从Range的一段开始迭代
if exclude_end? # 如果当前的指针没有位于Range的结束点
while x < self.end # 开始循环直到当前的x小于结束点
yield x
x += step
end
else # 如果当前的指针位于Range的结束点
while x <= self.end # 开始循环直到下 <=结束点
yield x
x += step
end
end
end # 结束方法定义
end # 结束对类的修改

# 一个调用的例子
(0..10).by(10) {|x| print x} # 打印 "0246810"
#(0...10).by(2) {|x| print x} # 打印 "02468"

by 方法非常的方便不是吗?但是它不是必须的,因为Range对象实际上有一个step方法可以实现相同的功能,Ruby的核心类型非常丰富,实际上很多你需要的东西Ruby已经为你提供了,在后面的章节中我们会详细介绍Ruby的核心类库。

No comments: