lib/modalsupport/enumerable.rb in modalsupport-0.5.1 vs lib/modalsupport/enumerable.rb in modalsupport-0.5.2

- old
+ new

@@ -40,109 +40,72 @@ else enum_for(:each_product_pair, other) end end - if_ruby_version(:<, '1.8.7') do - require 'generator' - - # Paralell iteration through multiple enumerable objects - # (1..3).paralell_each([:a,:b,:c],(10..12)).to_a - # => [[1, :a, 10], [2, :b, 11], [3, :c, 12]] - def paralell_each(*enums) - if block_given? - iterators = ([self]+enums).map{|e| Generator.new(e)} - loop { - begin - yield iterators.map{|e| e.next} - rescue EOFError - break - end - } - else - enum_for(:paralell_each, *enums) - end + # Paralell iteration through multiple enumerable objects + # (1..3).paralell_each([:a,:b,:c],(10..12)).to_a + # => [[1, :a, 10], [2, :b, 11], [3, :c, 12]] + # + # e.paralell_each(*enums) = e.zip(*enums).each + def paralell_each(*enums, &blk) + # Note that to implement a lazy iterator we'd need + # external iterators which don't work in Rubinius, are + # implemented differently in Ruby < 1.8.7 and are slow. + # So, for the time being we generate an array and then + # iterate it. + if block_given? + zip(*enums).each(&blk) + else + enum_for(:paralell_each, *enums) end - - # Cross-product iteration through multiple enumerable objects - # (1..4).cross_each([:a,:b,:c],(11..12)).to_a - # => [[1, :a, 11], [1, :a, 12], [1, :b, 11], [1, :b, 12], [1, :c, 11], [1, :c, 12], - # [2, :a, 11], [2, :a, 12], [2, :b, 11], [2, :b, 12], [2, :c, 11], [2, :c, 12], - # [3, :a, 11], [3, :a, 12], [3, :b, 11], [3, :b, 12], [3, :c, 11], [3, :c, 12], - # [4, :a, 11], [4, :a, 12], [4, :b, 11], [4, :b, 12], [4, :c, 11], [4, :c, 12]] - def cross_each(*enumerables) - if block_given? - enumerators = ([self]+enumerables).map{|e| Generator.new(e)} - values = enumerators.map{|e| e.next rescue nil} - yield values.dup - i = values.size - 1 - loop do - if enumerators[i].next? - values[i] = enumerators[i].next - yield values.dup # we could leave dupping up to the user, but .to_a would fail - i = values.size - 1 - else - enumerators[i].rewind - values[i] = enumerators[i].next - i -= 1 - if i<0 - break - end + end + + # Equivalent to paralell_each(*enums).to_a, but more efficient + def paralell_array(*enums) + zip(*enums) + end + + # Cross-product iteration through multiple enumerable objects + # (1..4).cross_each([:a,:b,:c],(11..12)).to_a + # => [[1, :a, 11], [1, :a, 12], [1, :b, 11], [1, :b, 12], [1, :c, 11], [1, :c, 12], + # [2, :a, 11], [2, :a, 12], [2, :b, 11], [2, :b, 12], [2, :c, 11], [2, :c, 12], + # [3, :a, 11], [3, :a, 12], [3, :b, 11], [3, :b, 12], [3, :c, 11], [3, :c, 12], + # [4, :a, 11], [4, :a, 12], [4, :b, 11], [4, :b, 12], [4, :c, 11], [4, :c, 12]] + # + # e.cross_each(*enums) = e.to_a.product(*enums.map{|enum| enum.to_a}).each + def cross_each(*enumerables) + if block_given? + enumerable, *rest = enumerables + self.each do |e| + if enumerables.empty? + yield [e] + else + enumerable.cross_each(*rest) do |rest_e| + yield [e]+rest_e end end - else - enum_for(:cross_each, *enumerables) end + else + enum_for(:cross_each, *enumerables) end - end - - if_ruby_version(:>=, '1.8.7') do - # Paralell iteration through multiple enumerable objects - # (1..3).paralell_each([:a,:b,:c],(10..12)).to_a - # => [[1, :a, 10], [2, :b, 11], [3, :c, 12]] - def paralell_each(*enums) - if block_given? - iterators = ([self]+enums).map{|e| e.to_enum} - loop { - yield iterators.map{|e| e.next} - } - else - enum_for(:paralell_each, *enums) - end - end - - # Cross-product iteration through multiple enumerable objects - # (1..4).cross_each([:a,:b,:c],(11..12)).to_a - # => [[1, :a, 11], [1, :a, 12], [1, :b, 11], [1, :b, 12], [1, :c, 11], [1, :c, 12], - # [2, :a, 11], [2, :a, 12], [2, :b, 11], [2, :b, 12], [2, :c, 11], [2, :c, 12], - # [3, :a, 11], [3, :a, 12], [3, :b, 11], [3, :b, 12], [3, :c, 11], [3, :c, 12], - # [4, :a, 11], [4, :a, 12], [4, :b, 11], [4, :b, 12], [4, :c, 11], [4, :c, 12]] - def cross_each(*enumerables) - if block_given? - enumerators = ([self]+enumerables).map{|e| e.to_enum} - values = enumerators.map{|e| e.next rescue nil} - yield values.dup - i = values.size - 1 - loop do - begin - values[i] = enumerators[i].next - rescue StopIteration - enumerators[i].rewind - values[i] = enumerators[i].next - i -= 1 - if i<0 - break - end - else - yield values.dup # we could leave dupping up to the user, but .to_a would fail - i = values.size - 1 - end + + # equivalent to cross_each(*enumerables).to_a, but more efficient + def cross_array(*enumerables) + # return to_a.product(*enumerables.map{|e| e.to_a}) + enumerables.unshift self + result = [[]] + while !enumerables.empty? + t, result = result, [] + b, *enumerables = enumerables + t.each do |a| + b.each do |n| + result << a + [n] end - else - enum_for(:cross_each, *enumerables) end end + result end - + end