块作为迭代器
如前所述,Rub y中块的主要用途之一是提供可以传递范围或项列表的迭代器。许多标准类(如 Integer 和 Array)都有方法可以提供块来迭代元素。例如:
3.times{ |i| puts( i ) }
[1,2,3].each{|i| puts(i) }
当然,你可以创建自己的迭代器方法,以便为块提供一系列值。在 iterate1.rb 程序中,我定义了一个简单的 timesRepeat
方法以执行指定次数的块代码。这类似 Integer 类的 times
方法,除了它从索引 1 而不是索引 0 开始的事实(这里显示变量 i
是正为了证明这一事实):
def timesRepeat( aNum )
for i in 1..aNum do
yield i
end
end
以下是如何调用此方法的示例:
timesRepeat( 3 ){ |i| puts("[#{i}] hello world") }
我还创建了一个 timesRepeat2
方法来迭代数组:
def timesRepeat2( aNum, anArray )
anArray.each{ |anitem|
yield( anitem )
}
end
这可以通过以下方式调用:
timesRepeat2( 3, ["hello","good day","how do you do"] ){ |x| puts(x) }
事实上,如果一个对象本身包含它自己的迭代器方法,那么它将更好(面向对象的灵魂更真实)。我在下一个示例中实现了这一点。在这里,我创建了 MyArray,作为 Array 的子类:
class MyArray < Array
在创建新的 MyArray 对象时,它使用数组初始化:
def initialize( anArray )
super( anArray )
end
它向上依赖于它自己的(self)each
方法,它由它的祖先 Array 提供,以迭代数组中的所有元素,并使用 Integer 的 times 方法执行此操作一定次数。这是完整的类定义:
class MyArray < Array
def initialize( anArray )
super( anArray )
end
def timesRepeat( aNum )
aNum.times{ # start block 1...
| num |
self.each{ # start block 2...
| anitem |
yield( "[#{num}] :: '#{anitem}'" )
} # ...end block 2
} # ...end block 1
end
end
请注意,由于我使用了两个迭代器(aNum.times
和 self.each
),因此 timesRepeat
方法包含两个嵌套块。这是一个如何使用它的示例…
numarr = MyArray.new( [1,2,3] )
numarr.timesRepeat( 2 ){ |x| puts(x) }
这将输出以下内容:
[0] :: '1'
[0] :: '2'
[0] :: '3'
[1] :: '1'
[1] :: '2'
[1] :: '3'
在 iterate3.rb 中,我自己设置了为包含任意数量的子数组的数组定义迭代器的问题,其中每个子数组具有相同数量的项。换句话说,它像一个具有固定行数和固定列数的表或矩阵。例如,这里是一个具有三个“行”(rows,子数组)和四个“列”(columns,元素)的多维数组:
multiarr =
[ ['one','two','three','four'],
[1, 2, 3, 4 ],
[:a, :b, :c, :d ]
]
我已经尝试了三个替代版本。第一个版本受到限制,只能使用在预定义数量(这里是索引 [0] 和 [1])的”行数”(rows)中:
multiarr[0].length.times{|i|
puts(multiarr[0][i], multiarr[1][i])
}
第二个版本通过迭代 multiarr
的每个元素(或’行’,row)然后通过获取行长度并使用 Integer 的 times
方法和该值迭代该行中的每个元素来绕过此限制:
multiarr.each{ |arr|
multiarr[0].length.times{|i|
puts(arr[i])
}
}
第三个版本反转这些操作:外部块沿着行 0 的长度迭代,内部块获得每行中索引 i
的元素:
multiarr[0].length.times{|i|
multiarr.each{ |arr|
puts(arr[i])
}
}
虽然版本 2 和版本 3 以类似的方式工作,但你会发现它们以不同的顺序迭代这些元素项目。运行该程序以验证。你可以尝试创建自己的 Array 子类并添加像这样的迭代器方法 - 一个按顺序迭代行的方法(如上面的版本 2)和一个按顺序遍历列的方法(如版本 3)。