Feature #18685
Updated by Eregon (Benoit Daloze) over 2 years ago
I'd like to add a new Enumerator class method for generating the Cartesian product of given enumerators. A product here does not mean an accumulated array of arrays, but an enumerator to enumerate all combinations. ```ruby product = Enumerator.product(1..3, ["A", "B"]) p product.class #=> Enumerator product.each do |i, c| puts "#{i}-#{c}" end =begin output 1-A 1-B 2-A 2-B 3-A 3-B =end ``` This can be used to reduce nested blocks and allows for iterating over an indefinite number of enumerable objects. ## Implementation notes - It should internally use `each_entry` instead of `each` on enumerable objects to make sure to capture all yielded arguments. - If no enumerable object is given, the block is called once with no argument. - It should reject a keyword-style hash argument so we can add keyword arguments in the future without breaking existing code. - Here's an example implementation: ```ruby # call-seq: # Enumerator.product(*enums) -> enum # Enumerator.product(*enums) { |*args| block } -> return value of args[0].each_entry {} def Enumerator.product(*enums, **nil, **kw, &block) kw.empty? or raise ArgumentError, "unknown keyword#{"s" if kw.size > 1}: #{kw.keys.map(&:inspect).join(", ")}" # TODO: size should be calculated if possible return to_enum(__method__, *enums, **kw) if block.nil? enums.reverse.reduce(block) { |inner, enum| ->(*values) { enum.each_entry { |value| inner.call(*values, value) } } }.call() end ``` - Not to be confused with `Enumerator.produce`. 😝 ## Prior case - Python: https://docs.python.org/3/library/itertools.html#itertools.product