何为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,因此它的实现就是直接迭代。因此他就更高效.