Feature #9826 » slice_when.patch
| enumerator.c (working copy) | ||
|---|---|---|
|     rb_define_method(rb_cLazy, "chunk", lazy_super, -1); | ||
|     rb_define_method(rb_cLazy, "slice_before", lazy_super, -1); | ||
|     rb_define_method(rb_cLazy, "slice_after", lazy_super, -1); | ||
|     rb_define_method(rb_cLazy, "slice_when", lazy_super, -1); | ||
|     rb_define_alias(rb_cLazy, "force", "to_a"); | ||
| enum.c (working copy) | ||
|---|---|---|
|     return enumerator; | ||
| } | ||
| struct slicewhen_arg { | ||
|     VALUE pred; | ||
|     VALUE prev_elt; | ||
|     VALUE prev_elts; | ||
|     VALUE yielder; | ||
| }; | ||
| static VALUE | ||
| slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) | ||
| { | ||
| #define UPDATE_MEMO ((void)(memo = MEMO_FOR(struct slicewhen_arg, _memo))) | ||
|     struct slicewhen_arg *memo; | ||
|     int split_p; | ||
|     UPDATE_MEMO; | ||
|     ENUM_WANT_SVALUE(); | ||
|     if (memo->prev_elt == Qundef) { | ||
|         /* The first element */ | ||
|         memo->prev_elt = i; | ||
|         memo->prev_elts = rb_ary_new3(1, i); | ||
|     } | ||
|     else { | ||
|         split_p = RTEST(rb_funcall(memo->pred, id_call, 2, memo->prev_elt, i)); | ||
|         UPDATE_MEMO; | ||
|         if (split_p) { | ||
|             rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts); | ||
|             UPDATE_MEMO; | ||
|             memo->prev_elts = rb_ary_new3(1, i); | ||
|         } | ||
|         else { | ||
|             rb_ary_push(memo->prev_elts, i); | ||
|         } | ||
|         memo->prev_elt = i; | ||
|     } | ||
|     return Qnil; | ||
| #undef UPDATE_MEMO | ||
| } | ||
| static VALUE | ||
| slicewhen_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator)) | ||
| { | ||
|     VALUE enumerable; | ||
|     VALUE arg; | ||
|     struct slicewhen_arg *memo = NEW_MEMO_FOR(struct slicewhen_arg, arg); | ||
|     enumerable = rb_ivar_get(enumerator, rb_intern("slicewhen_enum")); | ||
|     memo->pred = rb_attr_get(enumerator, rb_intern("slicewhen_pred")); | ||
|     memo->prev_elt = Qundef; | ||
|     memo->prev_elts = Qnil; | ||
|     memo->yielder = yielder; | ||
|     rb_block_call(enumerable, id_each, 0, 0, slicewhen_ii, arg); | ||
|     memo = MEMO_FOR(struct slicewhen_arg, arg); | ||
|     if (!NIL_P(memo->prev_elts)) | ||
|         rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts); | ||
|     return Qnil; | ||
| } | ||
| /* | ||
|  *  call-seq: | ||
|  *     enum.slice_when {|elt_before, elt_after| bool } -> an_enumerator | ||
|  * | ||
|  *  Creates an enumerator for each chunked elements. | ||
|  *  The beginnings of chunks are defined by the block. | ||
|  * | ||
|  *  This method split each chunk using adjacent elements, | ||
|  *  _elt_before_ and _elt_after_, | ||
|  *  in the receiver enumerator. | ||
|  *  This method split chunks between _elt_before_ and _elt_after_ where | ||
|  *  the block returns true. | ||
|  * | ||
|  *  The block is called the length of the receiver enumerator minus one. | ||
|  * | ||
|  *  The result enumerator yields the chunked elements as an array. | ||
|  *  So +each+ method can be called as follows: | ||
|  * | ||
|  *    enum.slice_when { |elt_before, elt_after| bool }.each { |ary| ... } | ||
|  * | ||
|  *  Other methods of the Enumerator class and Enumerable module, | ||
|  *  such as +map+, etc., are also usable. | ||
|  * | ||
|  *  For example, one-by-one increasing subsequence can be chunked as follows: | ||
|  * | ||
|  *    a = [1,2,4,9,10,11,12,15,16,19,20,21] | ||
|  *    b = a.slice_when {|i, j| i+1 != j } | ||
|  *    p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]] | ||
|  *    c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" } | ||
|  *    p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"] | ||
|  *    d = c.join(",") | ||
|  *    p d #=> "1,2,4,9-12,15,16,19-21" | ||
|  * | ||
|  *  Increasing (non-decreasing) subsequence can be chunked as follows: | ||
|  * | ||
|  *    a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5] | ||
|  *    p a.slice_when {|i, j| i > j }.to_a | ||
|  *    #=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]] | ||
|  * | ||
|  *  Adjacent evens and odds can be chunked as follows: | ||
|  *  (Enumerable#chunk is another way to do it.) | ||
|  * | ||
|  *    a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0] | ||
|  *    p a.slice_when {|i, j| i.even? != j.even? }.to_a | ||
|  *    #=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]] | ||
|  * | ||
|  *  Paragraphs (non-empty lines with trailing empty lines) can be chunked as follows: | ||
|  *  (See Enumerable#chunk to ignore empty lines.) | ||
|  * | ||
|  *    lines = ["foo\n", "bar\n", "\n", "baz\n", "qux\n"] | ||
|  *    p lines.slice_when {|l1, l2| /\A\s*\z/ =~ l1 && /\S/ =~ l2 }.to_a | ||
|  *    #=> [["foo\n", "bar\n", "\n"], ["baz\n", "qux\n"]] | ||
|  * | ||
|  */ | ||
| static VALUE | ||
| enum_slice_when(VALUE enumerable) | ||
| { | ||
|     VALUE enumerator; | ||
|     VALUE pred; | ||
|     pred = rb_block_proc(); | ||
|     enumerator = rb_obj_alloc(rb_cEnumerator); | ||
|     rb_ivar_set(enumerator, rb_intern("slicewhen_enum"), enumerable); | ||
|     rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred); | ||
|     rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator); | ||
|     return enumerator; | ||
| } | ||
| /* | ||
|  *  The <code>Enumerable</code> mixin provides collection classes with | ||
|  *  several traversal and searching methods, and with the ability to | ||
| ... | ... | |
|     rb_define_method(rb_mEnumerable, "chunk", enum_chunk, -1); | ||
|     rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, -1); | ||
|     rb_define_method(rb_mEnumerable, "slice_after", enum_slice_after, -1); | ||
|     rb_define_method(rb_mEnumerable, "slice_when", enum_slice_when, 0); | ||
|     id_next = rb_intern("next"); | ||
|     id_call = rb_intern("call"); | ||
| test/ruby/test_enum.rb (working copy) | ||
|---|---|---|
|     assert_equal([["foo", ""], ["bar"]], e.to_a) | ||
|   end | ||
|   def test_slice_when_0 | ||
|     e = [].slice_when {|a, b| flunk "should not be called" } | ||
|     assert_equal([], e.to_a) | ||
|   end | ||
|   def test_slice_when_1 | ||
|     e = [1].slice_when {|a, b| flunk "should not be called" } | ||
|     assert_equal([[1]], e.to_a) | ||
|   end | ||
|   def test_slice_when_2 | ||
|     e = [1,2].slice_when {|a,b| | ||
|       assert_equal(1, a) | ||
|       assert_equal(2, b) | ||
|       true | ||
|     } | ||
|     assert_equal([[1], [2]], e.to_a) | ||
|     e = [1,2].slice_when {|a,b| | ||
|       assert_equal(1, a) | ||
|       assert_equal(2, b) | ||
|       false | ||
|     } | ||
|     assert_equal([[1, 2]], e.to_a) | ||
|   end | ||
|   def test_slice_when_3 | ||
|     block_invocations = [ | ||
|       lambda {|a, b| | ||
|         assert_equal(1, a) | ||
|         assert_equal(2, b) | ||
|         true | ||
|       }, | ||
|       lambda {|a, b| | ||
|         assert_equal(2, a) | ||
|         assert_equal(3, b) | ||
|         false | ||
|       } | ||
|     ] | ||
|     e = [1,2,3].slice_when {|a,b| | ||
|       block_invocations.shift.call(a, b) | ||
|     } | ||
|     assert_equal([[1], [2, 3]], e.to_a) | ||
|     assert_equal([], block_invocations) | ||
|   end | ||
|   def test_slice_when_noblock | ||
|     assert_raise(ArgumentError) { [].slice_when } | ||
|   end | ||
|   def test_slice_when_contiguously_increasing_integers | ||
|     e = [1,4,9,10,11,12,15,16,19,20,21].slice_when {|i, j| i+1 != j } | ||
|     assert_equal([[1], [4], [9,10,11,12], [15,16], [19,20,21]], e.to_a) | ||
|   end | ||
|   def test_detect | ||
|     @obj = ('a'..'z') | ||
|     assert_equal('c', @obj.detect {|x| x == 'c' }) | ||
| test/ruby/test_lazy_enumerator.rb (working copy) | ||
|---|---|---|
|       assert_equal Enumerator::Lazy, [].lazy.send(method, *arg).class, bug7507 | ||
|     end | ||
|     assert_equal Enumerator::Lazy, [].lazy.chunk{}.class, bug7507 | ||
|     assert_equal Enumerator::Lazy, [].lazy.slice_when{}.class, bug7507 | ||
|   end | ||
|   def test_no_warnings | ||
- « Previous
- 1
- 2
- 3
- 4
- Next »