Implement Enumerator#+ and Enumerable#chain [Feature #15144]

They return an Enumerator::Chain object which is a subclass of
Enumerator, which represents a chain of enumerables that works as a
single enumerator.

```ruby
e = (1..3).chain([4, 5])
e.to_a #=> [1, 2, 3, 4, 5]

e = (1..3).each + [4, 5]
e.to_a #=> [1, 2, 3, 4, 5]
```

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65949 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2018-11-24 08:38:35 +00:00
parent c0e20037f3
commit 045b0e54d8
2 changed files with 429 additions and 1 deletions

View file

@ -670,5 +670,119 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([0, 1], u.force)
assert_equal([0, 1], u.force)
end
end
def test_enum_chain_and_plus
r = 1..5
e1 = r.chain()
assert_kind_of(Enumerator::Chain, e1)
assert_equal(5, e1.size)
ary = []
e1.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5], ary)
e2 = r.chain([6, 7, 8])
assert_kind_of(Enumerator::Chain, e2)
assert_equal(8, e2.size)
ary = []
e2.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
e3 = r.chain([6, 7], 8.step)
assert_kind_of(Enumerator::Chain, e3)
assert_equal(Float::INFINITY, e3.size)
ary = []
e3.take(10).each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
# `a + b + c` should not return `Enumerator::Chain.new(a, b, c)`
# because it is expected that `(a + b).each` be called.
e4 = e2.dup
class << e4
attr_reader :each_is_called
def each
super
@each_is_called = true
end
end
e5 = e4 + 9.step
assert_kind_of(Enumerator::Chain, e5)
assert_equal(Float::INFINITY, e5.size)
ary = []
e5.take(10).each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
assert_equal(true, e4.each_is_called)
end
def test_chained_enums
a = (1..5).each
e0 = Enumerator::Chain.new()
assert_kind_of(Enumerator::Chain, e0)
assert_equal(0, e0.size)
ary = []
e0.each { |x| ary << x }
assert_equal([], ary)
e1 = Enumerator::Chain.new(a)
assert_kind_of(Enumerator::Chain, e1)
assert_equal(5, e1.size)
ary = []
e1.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5], ary)
e2 = Enumerator::Chain.new(a, [6, 7, 8])
assert_kind_of(Enumerator::Chain, e2)
assert_equal(8, e2.size)
ary = []
e2.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
e3 = Enumerator::Chain.new(a, [6, 7], 8.step)
assert_kind_of(Enumerator::Chain, e3)
assert_equal(Float::INFINITY, e3.size)
ary = []
e3.take(10).each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
e4 = Enumerator::Chain.new(a, Enumerator.new { |y| y << 6 << 7 << 8 })
assert_kind_of(Enumerator::Chain, e4)
assert_equal(nil, e4.size)
ary = []
e4.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
e5 = Enumerator::Chain.new(e1, e2)
assert_kind_of(Enumerator::Chain, e5)
assert_equal(13, e5.size)
ary = []
e5.each { |x| ary << x }
assert_equal([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8], ary)
rewound = []
e1.define_singleton_method(:rewind) { rewound << object_id }
e2.define_singleton_method(:rewind) { rewound << object_id }
e5.rewind
assert_equal(rewound, [e2.object_id, e1.object_id])
rewound = []
a = [1]
e6 = Enumerator::Chain.new(a)
a.define_singleton_method(:rewind) { rewound << object_id }
e6.rewind
assert_equal(rewound, [])
assert_equal(
'#<Enumerator::Chain: [' +
'#<Enumerator::Chain: [' +
'#<Enumerator: 1..5:each>' +
']>, ' +
'#<Enumerator::Chain: [' +
'#<Enumerator: 1..5:each>, ' +
'[6, 7, 8]' +
']>' +
']>',
e5.inspect
)
end
end