summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--enumerator.c22
-rw-r--r--test/ruby/test_enumerator.rb45
2 files changed, 67 insertions, 0 deletions
diff --git a/enumerator.c b/enumerator.c
index fcf49b3f63..43008bc170 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -2890,6 +2890,28 @@ producer_size(VALUE obj, VALUE args, VALUE eobj)
return DBL2NUM(HUGE_VAL);
}
+/*
+ * call-seq:
+ * Enumerator.produce(initial = nil) { |val| } -> enumerator
+ *
+ * Creates an infinite enumerator from any block, just called over and
+ * over. Result of the previous iteration is passed to the next one.
+ * If +initial+ is provided, it is passed to the first iteration, and
+ * becomes the first element of the enumerator; if it is not provided,
+ * first iteration receives +nil+, and its result becomes first
+ * element of the iterator.
+ *
+ * Raising StopIteration from the block stops an iteration.
+ *
+ * Examples of usage:
+ *
+ * Enumerator.produce(1, &:succ) # => enumerator of 1, 2, 3, 4, ....
+ *
+ * Enumerator.produce { rand(10) } # => infinite random number sequence
+ *
+ * ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration }
+ * enclosing_section = ancestors.find { |n| n.type == :section }
+ */
static VALUE
enumerator_s_produce(int argc, VALUE *argv, VALUE klass)
{
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 54dfebb814..1e306c33a2 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -811,4 +811,49 @@ class TestEnumerator < Test::Unit::TestCase
e5.inspect
)
end
+
+ def test_produce
+ assert_raise(ArgumentError) { Enumerator.produce }
+
+ # Without initial object
+ passed_args = []
+ enum = Enumerator.produce { |obj| passed_args << obj; (obj || 0).succ }
+ assert_instance_of(Enumerator, enum)
+ assert_equal Float::INFINITY, enum.size
+ assert_equal [1, 2, 3], enum.take(3)
+ assert_equal [nil, 1, 2], passed_args
+
+ # With initial object
+ passed_args = []
+ enum = Enumerator.produce(1) { |obj| passed_args << obj; obj.succ }
+ assert_instance_of(Enumerator, enum)
+ assert_equal Float::INFINITY, enum.size
+ assert_equal [1, 2, 3], enum.take(3)
+ assert_equal [1, 2], passed_args
+
+ # Raising StopIteration
+ words = "The quick brown fox jumps over the lazy dog.".scan(/\w+/)
+ enum = Enumerator.produce { words.shift or raise StopIteration }
+ assert_equal Float::INFINITY, enum.size
+ assert_instance_of(Enumerator, enum)
+ assert_equal %w[The quick brown fox jumps over the lazy dog], enum.to_a
+
+ # Raising StopIteration
+ object = [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"]
+ enum = Enumerator.produce(object) { |obj|
+ obj.respond_to?(:first) or raise StopIteration
+ obj.first
+ }
+ assert_equal Float::INFINITY, enum.size
+ assert_instance_of(Enumerator, enum)
+ assert_nothing_raised {
+ assert_equal [
+ [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"],
+ [[["abc", "def"], "ghi", "jkl"], "mno", "pqr"],
+ [["abc", "def"], "ghi", "jkl"],
+ ["abc", "def"],
+ "abc",
+ ], enum.to_a
+ }
+ end
end