Ruby doesn't seem to have many built-in methods for doing the different things you wanted to do with the counters, but you can create your own methods. This is what I did here using Ruby 1.9:
iter.rb
def get_enums_from_args(args) args.collect { |e| e.is_a?(Enumerator) ? e.dup : e.to_enum } end def build(y, &block) while true y << (begin yield; rescue StopIteration; break; end) end end def zip(*args) enums = get_enums_from_args args Enumerator.new do |y| build y do enums.collect { |e| e.next } end end end def chain(*args) enums = get_enums_from_args args Enumerator.new do |y| enums.each do |e| build y do e.next end end end end def multiply(*args) enums = get_enums_from_args args duped_enums = enums.collect { |e| e.dup } Enumerator.new do |y| begin while true y << (begin; enums.collect { |e| e.peek }; rescue StopIteration; break; end ) index = enums.length - 1 while true begin enums[index].next enums[index].peek break rescue StopIteration
And I wrote a specification using rspec to test functions and demonstrate what they do:
iter_spec.rb:
require_relative 'iter' describe "zip" do it "zips together enumerators" do e1 = "Louis".chars e2 = "198".chars zip(e1,e2).to_a.should == [ ['L','1'], ['o','9'], ['u','8'] ] end it "works with arrays too" do zip([1,2], [:a, nil]).to_a.should == [ [1,:a], [2,nil] ] end end describe "chain" do it "chains enumerators" do e1 = "Jon".chars e2 = 0..99999999999 e = chain(e1, e2) e.next.should == "J" e.next.should == "o" e.next.should == "n" e.next.should == 0 e.next.should == 1 end end describe "multiply" do it "multiplies enumerators" do e1 = "ABC".chars e2 = 1..3 multiply(e1, e2).to_a.should == [["A", 1], ["A", 2], ["A", 3], ["B", 1], ["B", 2], ["B", 3], ["C", 1], ["C", 2], ["C", 3]] end it "is lazily evalutated" do e1 = 0..999999999 e2 = 1..3 e = multiply(e1, e2) e.next.should == [0, 1] e.next.should == [0, 2] e.next.should == [0, 3] e.next.should == [1, 1] e.next.should == [1, 2] end it "resulting enumerator can not be cloned effectively" do ranks = chain(2..10, [:jack, :queen, :king, :ace]) suits = [:clubs, :diamonds, :hearts, :spades] cards = multiply(suits, ranks) c2 = cards.clone cards.next.should == [:clubs, 2] c2.next.should == [:clubs, 2] c2.next.should == [:clubs, 3] c2.next.should == [:clubs, 4] c2.next.should == [:clubs, 5] cards.next.should == [:clubs, 6] end it "resulting enumerator can not be duplicated after first item is evaluated" do ranks = chain(2..10, [:jack, :queen, :king, :ace]) suits = [:clubs, :diamonds, :hearts, :spades] cards = multiply(ranks, suits) cards.peek lambda { cards.dup }.should raise_error TypeError end end
As shown in the above specifications, these methods use lazy evaluation.
In addition, the main weakness of the zip , chain and multiply functions defined here is that the resulting counter cannot be easily duplicated or cloned, because we did not write code to duplicate the listing arguments that these new counters rely on. You probably need to subclass Enumerator or make a class by including an Enumerable module or something similar to make dup work beautifully.