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:
Post a Comment