[前][次][番号順一覧][スレッド一覧]

ruby-changes:38808

From: akr <ko1@a...>
Date: Sun, 14 Jun 2015 17:54:24 +0900 (JST)
Subject: [ruby-changes:38808] akr:r50889 (trunk): * enum.c (enum_chunk_while): New method Enumerable#chunk_while.

akr	2015-06-14 17:54:19 +0900 (Sun, 14 Jun 2015)

  New Revision: 50889

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=50889

  Log:
    * enum.c (enum_chunk_while): New method Enumerable#chunk_while.
      [ruby-core:67738] [Feature #10769] proposed by Tsuyoshi Sawada.

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/enum.c
    trunk/test/ruby/test_enum.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 50888)
+++ ChangeLog	(revision 50889)
@@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sun Jun 14 17:26:03 2015  Tanaka Akira  <akr@f...>
+
+	* enum.c (enum_chunk_while): New method Enumerable#chunk_while.
+	  [ruby-core:67738] [Feature #10769] proposed by Tsuyoshi Sawada.
+
 Sun Jun 14 17:20:40 2015  Nobuyoshi Nakada  <nobu@r...>
 
 	* file.c (rb_file_load_ok): try opening file without gvl not to
Index: enum.c
===================================================================
--- enum.c	(revision 50888)
+++ enum.c	(revision 50889)
@@ -3255,6 +3255,7 @@ struct slicewhen_arg { https://github.com/ruby/ruby/blob/trunk/enum.c#L3255
     VALUE prev_elt;
     VALUE prev_elts;
     VALUE yielder;
+    int inverted; /* 0 for slice_when and 1 for chunk_while. */
 };
 
 static VALUE
@@ -3276,6 +3277,9 @@ slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST( https://github.com/ruby/ruby/blob/trunk/enum.c#L3277
         split_p = RTEST(rb_funcall(memo->pred, id_call, 2, memo->prev_elt, i));
         UPDATE_MEMO;
 
+        if (memo->inverted)
+            split_p = !split_p;
+
         if (split_p) {
             rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
             UPDATE_MEMO;
@@ -3304,6 +3308,7 @@ slicewhen_i(RB_BLOCK_CALL_FUNC_ARGLIST(y https://github.com/ruby/ruby/blob/trunk/enum.c#L3308
     memo->prev_elt = Qundef;
     memo->prev_elts = Qnil;
     memo->yielder = yielder;
+    memo->inverted = RTEST(rb_attr_get(enumerator, rb_intern("slicewhen_inverted")));
 
     rb_block_call(enumerable, id_each, 0, 0, slicewhen_ii, arg);
     memo = MEMO_FOR(struct slicewhen_arg, arg);
@@ -3383,6 +3388,71 @@ enum_slice_when(VALUE enumerable) https://github.com/ruby/ruby/blob/trunk/enum.c#L3388
     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_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qfalse);
+
+    rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
+    return enumerator;
+}
+
+/*
+ *  call-seq:
+ *     enum.chunk_while {|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 false.
+ *
+ *  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.chunk_while { |elt_before, elt_after| bool }.each { |ary| ... }
+ *
+ *  Other methods of the Enumerator class and Enumerable module,
+ *  such as +to_a+, +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.chunk_while {|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.chunk_while {|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.chunk_while {|i, j| i.even? == j.even? }.to_a
+ *    #=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]]
+ *
+ */
+static VALUE
+enum_chunk_while(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_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qtrue);
 
     rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
     return enumerator;
@@ -3459,6 +3529,7 @@ Init_Enumerable(void) https://github.com/ruby/ruby/blob/trunk/enum.c#L3529
     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);
+    rb_define_method(rb_mEnumerable, "chunk_while", enum_chunk_while, 0);
 
     id_next = rb_intern("next");
     id_call = rb_intern("call");
Index: NEWS
===================================================================
--- NEWS	(revision 50888)
+++ NEWS	(revision 50889)
@@ -19,6 +19,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L19
 
   * Enumerable#grep_v is added as inverse version of Enumerable#grep.
     [Feature #11049]
+  * Enumerable#chunk_while [Feature #10769]
 
 * Numeric
 
Index: test/ruby/test_enum.rb
===================================================================
--- test/ruby/test_enum.rb	(revision 50888)
+++ test/ruby/test_enum.rb	(revision 50889)
@@ -659,6 +659,11 @@ class TestEnumerable < Test::Unit::TestC https://github.com/ruby/ruby/blob/trunk/test/ruby/test_enum.rb#L659
     assert_equal([[1], [4], [9,10,11,12], [15,16], [19,20,21]], e.to_a)
   end
 
+  def test_chunk_while_contiguously_increasing_integers
+    e = [1,4,9,10,11,12,15,16,19,20,21].chunk_while {|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' })

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]