在ruby1.9中增加了External Iterators这中新的迭代类型,所有的Enumerable 都是External Iterators.而在ruby1.9之前的版本中的迭代都是internal iterator。
何为external iterator呢,其实我认为也就是一个生成器了:
iterator = 9.downto(1) # An enumerator as external iterator
begin # So we can use rescue below
print iterator.next while true # Call the next method repeatedly
rescue StopIteration # When there are no more values
puts "...blastoff!" # An expected, nonexceptional condition
end
这里的Kernel.loop方法就是为了迭代External Iterators而专门创建的。当next方法到最后一个元素的时候,就会抛出StopIteration,因此这里我们捕捉这个异常来判断迭代是否结束.
任何External Iterators都可以调用rewind 方法来重启这个迭代器。要注意如果一个enumerator 基于像File这样的对象,那么调用rewind将不会起作用。
一旦一个External Iterators开始迭代,那么他就不能被clone和duplicated。
我们可以简单的使用External Iterators来创建一个each方法,也就是通过External Iterator来创建一个internal iterator:
module Iterable
include Enumerable # Define iterators on top of each
def each # And define each on top of next
loop { yield self.next }
end
end
下面我们可以这样做:
def iterate(iterator)
loop { yield iterator.next }
end
iterate(9.downto(1)) {|x| print x }
下面是三个函数,功能都是一样的,第一个使用internal iterators,第二个虽然使用了External iterators ,可是没有用loop方法,因此代码很丑陋,第三个方法是最好的方法。:
# Call the each method of each collection in turn.
# This is not a parallel iteration and does not require enumerators.
def sequence(*enumerables, &block)
enumerables.each do |enumerable|
enumerable.each(&block)
end
end
# Iterate the specified collections, interleaving their elements.
# This can't be done efficiently without external iterators.
# Note the use of the uncommon else clause in begin/rescue.
def interleave(*enumerables)
# Convert enumerable collections to an array of enumerators.
enumerators = enumerables.map {|e| e.to_enum }
# Loop until we don't have any more enumerators.
until enumerators.empty?
begin
e = enumerators.shift # Take the first enumerator
yield e.next # Get its next and pass to the block
rescue StopIteration # If no more elements, do nothing
else # If no exception occurred
enumerators << e # Put the enumerator back
end
end
end
# Iterate the specified collections, yielding tuples of values,
# one value from each of the collections. See also Enumerable.zip.
def bundle(*enumerables)
enumerators = enumerables.map {|e| e.to_enum }
loop { yield enumerators.map {|e| e.next} }
end
# Examples of how these iterator methods work
a,b,c = [1,2,3], 4..6, 'a'..'e'
sequence(a,b,c) {|x| print x} # prints "123456abcde"
interleave(a,b,c) {|x| print x} # prints "14a25b36cde"
bundle(a,b,c) {|x| print x} # '[1, 4, "a"][2, 5, "b"][3, 6, "c"]'
这里要注意,在ruby1.8中的zip效率很低,那是因为它首先转换参数为数组,然后迭代他,可是在1.9中,由于我们有了External Iterators,因此它的实现就是直接迭代。因此他就更高效.