|
class Enumerator
|
|
class From < Enumerator
|
|
def initialize(seeds, drop: 0, allow_nil: false, &block)
|
|
@block = block or
|
|
raise ArgumentError, "block not given"
|
|
@seeds = Array.try_convert(seeds) or
|
|
raise ArgumentError, "seeds must be an array"
|
|
drop.is_a?(Integer) && drop >= 0 or
|
|
raise ArgumentError, "drop must be a non-negative integer"
|
|
@drop = drop
|
|
allow_nil.equal?(true) || allow_nil.equal?(false) or
|
|
raise ArgumentError, "allow_nil must be boolean"
|
|
@allow_nil = allow_nil
|
|
end
|
|
|
|
def each
|
|
drop = @drop
|
|
allow_nil = @allow_nil
|
|
nterms = @seeds.size
|
|
block = @block
|
|
if drop < nterms
|
|
(drop...nterms).each { |i|
|
|
value = @seeds[i]
|
|
return if value.nil? && !allow_nil
|
|
yield value
|
|
}
|
|
drop = 0
|
|
else
|
|
drop -= nterms
|
|
end
|
|
preceding_terms = [*@seeds]
|
|
loop {
|
|
value = block.call(*preceding_terms)
|
|
break if value.nil? && !allow_nil
|
|
yield value if drop == 0
|
|
preceding_terms.shift
|
|
preceding_terms.push(value)
|
|
drop -= 1 if drop > 0
|
|
}
|
|
end
|
|
|
|
def size
|
|
nil
|
|
end
|
|
end
|
|
|
|
# Enumerator.from(seeds, after: 0, allow_nil: false) { |*preceding_terms|
|
|
# next_term
|
|
# }
|
|
def self.from(seeds, drop: 0, allow_nil: false, &block)
|
|
From.new(seeds, drop: drop, allow_nil: allow_nil, &block)
|
|
end
|
|
end
|
|
|
|
require 'test/unit'
|
|
require 'ostruct'
|
|
|
|
class TestEnumerator < Test::Unit::TestCase
|
|
def test_s_from
|
|
assert_kind_of(Enumerator, Enumerator.from([1], &:succ))
|
|
|
|
assert_equal([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], Enumerator.from([]) { 0 }.take(10))
|
|
|
|
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], Enumerator.from([1], &:succ).take(10))
|
|
|
|
assert_equal([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], Enumerator.from([0, 1]) { |i, j| i + j }.take(10))
|
|
assert_equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55], Enumerator.from([0, 1], drop: 1) { |i, j| i + j }.take(10))
|
|
assert_equal([1, 2, 3, 5, 8, 13, 21, 34, 55, 89], Enumerator.from([0, 1], drop: 2) { |i, j| i + j }.take(10))
|
|
assert_equal([2, 3, 5, 8, 13, 21, 34, 55, 89, 144], Enumerator.from([0, 1], drop: 3) { |i, j| i + j }.take(10))
|
|
|
|
root = OpenStruct.new(name: "Root", parent: nil)
|
|
node = ("A".."E").inject(root) { |parent, name|
|
|
OpenStruct.new(name: name, parent: parent)
|
|
}
|
|
|
|
assert_equal(["E", "D", "C", "B", "A", "Root"], Enumerator.from([node], &:parent).map(&:name))
|
|
assert_equal(["D", "C", "B", "A", "Root"], Enumerator.from([node], drop: 1, &:parent).map(&:name))
|
|
assert_equal(["C", "B", "A", "Root"], Enumerator.from([node], drop: 2, &:parent).map(&:name))
|
|
assert_equal(["B", "A", "Root"], Enumerator.from([node], drop: 3, &:parent).map(&:name))
|
|
assert_equal(["E", "D", "C", "B", "A", "Root", nil], Enumerator.from([node], allow_nil: true) { |n|
|
|
raise StopIteration if n.nil?
|
|
n.parent
|
|
}.map { |n| n&.name })
|
|
assert_equal([], Enumerator.from([nil], &:parent).map(&:name))
|
|
|
|
assert_equal([nil, nil, nil, nil, nil], Enumerator.from([nil], allow_nil: true, &:itself).take(5))
|
|
assert_equal([1, nil, 1, nil, 1], Enumerator.from([1, nil], allow_nil: true) { |i, j| i }.take(5))
|
|
end
|
|
end
|