* [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators
@ 2022-04-08 7:20 knu (Akinori MUSHA)
2022-04-08 10:52 ` [ruby-core:108199] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables Eregon (Benoit Daloze)
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: knu (Akinori MUSHA) @ 2022-04-08 7:20 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been reported by knu (Akinori MUSHA).
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerators
https://bugs.ruby-lang.org/issues/18685
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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 from enumerable objects.
- 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, **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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108199] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
@ 2022-04-08 10:52 ` Eregon (Benoit Daloze)
2022-04-21 7:59 ` [ruby-core:108334] " matz (Yukihiro Matsumoto)
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Eregon (Benoit Daloze) @ 2022-04-08 10:52 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by Eregon (Benoit Daloze).
Isn't it significantly slower than doing it manually due to all these extra block and method calls etc in between?
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97183
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, **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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108334] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
2022-04-08 10:52 ` [ruby-core:108199] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables Eregon (Benoit Daloze)
@ 2022-04-21 7:59 ` matz (Yukihiro Matsumoto)
2022-04-21 8:04 ` [ruby-core:108335] " knu (Akinori MUSHA)
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: matz (Yukihiro Matsumoto) @ 2022-04-21 7:59 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by matz (Yukihiro Matsumoto).
Looping class method is an unusual pattern in Enumerable. So I first hesitated, but actually it's the only way to go if we want to introduce `product'.
And the method seems to be convenient. Thus, I accept.
@eregon If the performance matters, we can re-implement it in C.
Matz.
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97359
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108335] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
2022-04-08 10:52 ` [ruby-core:108199] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables Eregon (Benoit Daloze)
2022-04-21 7:59 ` [ruby-core:108334] " matz (Yukihiro Matsumoto)
@ 2022-04-21 8:04 ` knu (Akinori MUSHA)
2022-04-21 10:37 ` [ruby-core:108346] " Eregon (Benoit Daloze)
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: knu (Akinori MUSHA) @ 2022-04-21 8:04 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by knu (Akinori MUSHA).
Thanks. Performance monsters can also special-case arrays and ranges and omit calls to each_entry to boost performance. 😂
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97360
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108346] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (2 preceding siblings ...)
2022-04-21 8:04 ` [ruby-core:108335] " knu (Akinori MUSHA)
@ 2022-04-21 10:37 ` Eregon (Benoit Daloze)
2022-04-21 10:40 ` [ruby-core:108347] " Eregon (Benoit Daloze)
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Eregon (Benoit Daloze) @ 2022-04-21 10:37 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by Eregon (Benoit Daloze).
My performance concern was not about Ruby vs C, writing in C would have the same issues.
What I'm saying is this:
```ruby
(1..3).each do |i|
["A", "B"].each do |c|
puts "#{i}-#{c}"
end
end
```
will always be faster than:
```ruby
Enumerator.product(1..3, ["A", "B"]).each do |i, c|
puts "#{i}-#{c}"
end
```
because the second has a generic/cannot-do-sensible-inline-cache loop and lots of array allocation and splatting.
So Enumerator.product makes sense when one doesn't know how many `enums` to combine, or a large number of them, but for 2-3 it's better performance-wise (and maybe also for clarity) to just use nested `each`.
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97371
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108347] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (3 preceding siblings ...)
2022-04-21 10:37 ` [ruby-core:108346] " Eregon (Benoit Daloze)
@ 2022-04-21 10:40 ` Eregon (Benoit Daloze)
2022-04-23 19:26 ` [ruby-core:108387] " shan (Shannon Skipper)
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Eregon (Benoit Daloze) @ 2022-04-21 10:40 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by Eregon (Benoit Daloze).
And between writing `Enumerator.product` in Ruby or C I'd prefer a lot in Ruby because it's a million times more readable, and it can be reused by other Ruby implementations :)
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97372
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108387] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (4 preceding siblings ...)
2022-04-21 10:40 ` [ruby-core:108347] " Eregon (Benoit Daloze)
@ 2022-04-23 19:26 ` shan (Shannon Skipper)
2022-04-24 2:36 ` [ruby-core:108388] " knu (Akinori MUSHA)
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: shan (Shannon Skipper) @ 2022-04-23 19:26 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by shan (Shannon Skipper).
It might also be nice to require at least one `enum` argument, since `Enumerator.product #=> [nil]` seems a bit odd. Here's a stab at lazy size:
``` ruby
def Enumerator.product(*enums, **nil, &block)
raise ArgumentError, 'wrong number of arguments (given 0, expected 1..)' if enums.empty?
unless block_given?
return to_enum(__method__, *enums) do
enums.reduce(1) do |acc, enum|
enum_size = enum.size
break unless enum_size
acc * enum_size
end
end
end
enums.reverse.reduce(block) do |inner, enum|
lambda do |*values|
enum.each_entry do |value|
inner.call(*values, value)
end
end
end.call
end
```
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97421
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108388] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (5 preceding siblings ...)
2022-04-23 19:26 ` [ruby-core:108387] " shan (Shannon Skipper)
@ 2022-04-24 2:36 ` knu (Akinori MUSHA)
2022-04-26 7:02 ` [ruby-core:108405] " sawa (Tsuyoshi Sawada)
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: knu (Akinori MUSHA) @ 2022-04-24 2:36 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by knu (Akinori MUSHA).
That's actually not a mathematical idea. The 0-ary Cartesian product of sets should be defined as a singleton set for theoretical and practical reasons. It's just like 2^0 equals to 1.
Python's itertools.product aligns with this theory.
```python
import itertools
for i in itertools.product(range(3), range(3)):
print("2-ary: " + repr(i))
for i in itertools.product(range(3)):
print("1-ary: " + repr(i))
for i in itertools.product():
print("0-ary: " + repr(i))
```
Output:
```
2-ary: (0, 0)
2-ary: (0, 1)
2-ary: (0, 2)
2-ary: (1, 0)
2-ary: (1, 1)
2-ary: (1, 2)
2-ary: (2, 0)
2-ary: (2, 1)
2-ary: (2, 2)
1-ary: (0,)
1-ary: (1,)
1-ary: (2,)
0-ary: ()
```
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97422
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:108405] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (6 preceding siblings ...)
2022-04-24 2:36 ` [ruby-core:108388] " knu (Akinori MUSHA)
@ 2022-04-26 7:02 ` sawa (Tsuyoshi Sawada)
2022-07-29 8:29 ` [ruby-core:109362] " knu (Akinori MUSHA)
2022-11-17 6:42 ` [ruby-core:110790] " hsbt (Hiroshi SHIBATA)
9 siblings, 0 replies; 11+ messages in thread
From: sawa (Tsuyoshi Sawada) @ 2022-04-26 7:02 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by sawa (Tsuyoshi Sawada).
Related to #6499 #7444 #8970 #14399
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-97440
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:109362] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (7 preceding siblings ...)
2022-04-26 7:02 ` [ruby-core:108405] " sawa (Tsuyoshi Sawada)
@ 2022-07-29 8:29 ` knu (Akinori MUSHA)
2022-11-17 6:42 ` [ruby-core:110790] " hsbt (Hiroshi SHIBATA)
9 siblings, 0 replies; 11+ messages in thread
From: knu (Akinori MUSHA) @ 2022-07-29 8:29 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by knu (Akinori MUSHA).
https://github.com/ruby/ruby/pull/6197
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-98506
* Author: knu (Akinori MUSHA)
* Status: Open
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ruby-core:110790] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
` (8 preceding siblings ...)
2022-07-29 8:29 ` [ruby-core:109362] " knu (Akinori MUSHA)
@ 2022-11-17 6:42 ` hsbt (Hiroshi SHIBATA)
9 siblings, 0 replies; 11+ messages in thread
From: hsbt (Hiroshi SHIBATA) @ 2022-11-17 6:42 UTC (permalink / raw)
To: ruby-core
Issue #18685 has been updated by hsbt (Hiroshi SHIBATA).
Status changed from Open to Closed
https://github.com/ruby/ruby/pull/6197 has been merged for Ruby 3.2
----------------------------------------
Feature #18685: Enumerator.product: Cartesian product of enumerables
https://bugs.ruby-lang.org/issues/18685#change-100142
* Author: knu (Akinori MUSHA)
* Status: Closed
* Priority: Normal
* Target version: 3.2
----------------------------------------
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, &block)
# TODO: size should be calculated if possible
return to_enum(__method__, *enums) 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
--
https://bugs.ruby-lang.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-11-17 6:42 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-08 7:20 [ruby-core:108198] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerators knu (Akinori MUSHA)
2022-04-08 10:52 ` [ruby-core:108199] [Ruby master Feature#18685] Enumerator.product: Cartesian product of enumerables Eregon (Benoit Daloze)
2022-04-21 7:59 ` [ruby-core:108334] " matz (Yukihiro Matsumoto)
2022-04-21 8:04 ` [ruby-core:108335] " knu (Akinori MUSHA)
2022-04-21 10:37 ` [ruby-core:108346] " Eregon (Benoit Daloze)
2022-04-21 10:40 ` [ruby-core:108347] " Eregon (Benoit Daloze)
2022-04-23 19:26 ` [ruby-core:108387] " shan (Shannon Skipper)
2022-04-24 2:36 ` [ruby-core:108388] " knu (Akinori MUSHA)
2022-04-26 7:02 ` [ruby-core:108405] " sawa (Tsuyoshi Sawada)
2022-07-29 8:29 ` [ruby-core:109362] " knu (Akinori MUSHA)
2022-11-17 6:42 ` [ruby-core:110790] " hsbt (Hiroshi SHIBATA)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).