[#38392] Enumerable#gather_each — Tanaka Akira <akr@...>

ときに、複数行をまとめて扱いたいことがあります。

47 messages 2009/05/09
[#38394] Re: Enumerable#gather_each — ujihisa <ujihisa@...> 2009/05/09

ujihisaと申します。

[#38400] Re: Enumerable#gather_each — Yukihiro Matsumoto <matz@...> 2009/05/09

まつもと ゆきひろです

[#38399] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/09

At Sat, 9 May 2009 15:30:20 +0900,

[#38405] Re: Enumerable#gather_each — Tanaka Akira <akr@...> 2009/05/10

In article <86r5yy2nrg.knu@iDaemons.org>,

[#38417] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/10

At Sun, 10 May 2009 10:08:47 +0900,

[#38524] [Bug #1503] -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる — sinnichi eguchi <redmine@...>

Bug #1503: -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる

8 messages 2009/05/22

[ruby-dev:38418] Re: Enumerable#gather_each

From: Tanaka Akira <akr@...>
Date: 2009-05-10 06:37:05 UTC
List: ruby-dev #38418
In article <871vqysrru.fsf@fsij.org>,
  Tanaka Akira <akr@fsij.org> writes:

> C での実装はまだですが、これからやる予定です。

とりあえず C で実装してみたのでつけておきます。

以下のメソッドを実装してあります。

* Enumerable#gather_each
* Enumerable#gather
* Enumerable#slice_before

slice_before は Yielder を使ってあります。

gather も Yielder を使う形にすれば、gather_each をなくすこと
は可能だと思います。

% svn diff --diff-cmd diff -x '-u -p'
Index: enumerator.c
===================================================================
--- enumerator.c	(revision 23381)
+++ enumerator.c	(working copy)
@@ -14,6 +14,18 @@
 
 #include "ruby/ruby.h"
 
+static VALUE
+enum_values_pack(int argc, VALUE *argv)
+{
+    if (argc == 0) return Qnil;
+    if (argc == 1) return argv[0];
+    return rb_ary_new4(argc, argv);
+}
+
+#define ENUM_WANT_SVALUE() do { \
+    i = enum_values_pack(argc, argv); \
+} while (0)
+
 /*
  * Document-class: Enumerator
  *
@@ -45,6 +57,7 @@ struct yielder {
     VALUE proc;
 };
 
+static VALUE enumerator_allocate(VALUE klass);
 static VALUE generator_allocate(VALUE klass);
 static VALUE generator_init(VALUE obj, VALUE proc);
 
@@ -247,6 +260,61 @@ enum_each_with_object(VALUE obj, VALUE m
     return memo;
 }
 
+struct slice_before_arg {
+    VALUE separator_p;
+    VALUE prev_elts;
+    VALUE yielder;
+};
+
+static VALUE
+slice_before_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
+{
+    struct slice_before_arg *argp = (struct slice_before_arg *)_argp;
+
+    ENUM_WANT_SVALUE();
+
+    if (RTEST(rb_funcall(argp->separator_p, rb_intern("call"), 1, i))) {
+        if (!NIL_P(argp->prev_elts))
+            rb_funcall(argp->yielder, rb_intern("<<"), 1, argp->prev_elts);
+        argp->prev_elts = rb_ary_new3(1, i);
+    }
+    else {
+        if (NIL_P(argp->prev_elts))
+            argp->prev_elts = rb_ary_new3(1, i);
+        else
+            rb_ary_push(argp->prev_elts, i);
+    }
+
+    return Qnil;
+}
+
+static VALUE
+slice_before_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
+{
+    VALUE enumerable;
+    struct slice_before_arg arg;
+
+    enumerable = rb_ivar_get(enumerator, rb_intern("slice_before_enumerable"));
+    arg.separator_p = rb_ivar_get(enumerator, rb_intern("slice_before_separator_p"));
+    arg.prev_elts = Qnil;
+    arg.yielder = yielder;
+
+    rb_block_call(enumerable, id_each, 0, 0, slice_before_ii, (VALUE)&arg);
+    if (!NIL_P(arg.prev_elts))
+        rb_funcall(arg.yielder, rb_intern("<<"), 1, arg.prev_elts);
+    return Qnil;
+}
+
+static VALUE
+enum_slice_before(VALUE enumerable)
+{
+    VALUE enumerator = enumerator_allocate(rb_cEnumerator);
+    rb_ivar_set(enumerator, rb_intern("slice_before_enumerable"), enumerable);
+    rb_ivar_set(enumerator, rb_intern("slice_before_separator_p"), rb_block_proc());
+    rb_block_call(enumerator, rb_intern("initialize"), 0, 0, slice_before_i, enumerator);
+    return enumerator;
+}
+
 static VALUE
 enumerator_allocate(VALUE klass)
 {
@@ -862,6 +930,8 @@ Init_Enumerator(void)
     rb_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1);
     rb_define_method(rb_mEnumerable, "each_with_object", enum_each_with_object, 1);
 
+    rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, 0);
+
     rb_cEnumerator = rb_define_class("Enumerator", rb_cObject);
     rb_include_module(rb_cEnumerator, rb_mEnumerable);
 
Index: enum.c
===================================================================
--- enum.c	(revision 23381)
+++ enum.c	(working copy)
@@ -1793,6 +1793,141 @@ enum_cycle(int argc, VALUE *argv, VALUE 
     return Qnil;		/* not reached */
 }
 
+struct gather_each_st {
+    VALUE arg;
+    VALUE prev_value;
+    VALUE prev_elts;
+};
+
+static VALUE
+gather_each_i(VALUE i, VALUE _state, int argc, VALUE *argv)
+{
+    struct gather_each_st *statep = (struct gather_each_st *)_state;
+    VALUE v;
+    VALUE singleton, reject;
+
+    ENUM_WANT_SVALUE();
+
+    v = rb_funcall(statep->arg, rb_intern("call"), 1, i);
+
+    singleton = ID2SYM(rb_intern("singleton"));
+    reject = ID2SYM(rb_intern("reject"));
+
+    if (v == singleton) {
+        if (!NIL_P(statep->prev_value)) {
+            rb_yield(statep->prev_elts);
+            statep->prev_value = statep->prev_elts = Qnil;
+        }
+        rb_yield(rb_ary_new3(1, i));
+    }
+    else if (!RTEST(v) || v == reject) {
+        if (!NIL_P(statep->prev_value)) {
+            rb_yield(statep->prev_elts);
+            statep->prev_value = statep->prev_elts = Qnil;
+        }
+    }
+    else {
+        if (NIL_P(statep->prev_value)) {
+            statep->prev_value = v;
+            statep->prev_elts = rb_ary_new3(1, i);
+        }
+        else {
+            if (rb_equal(statep->prev_value, v)) {
+                rb_ary_push(statep->prev_elts, i);
+            }
+            else {
+                rb_yield(statep->prev_elts);
+                statep->prev_value = v;
+                statep->prev_elts = rb_ary_new3(1, i);
+            }
+        }
+    }
+
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     enum.gather_each(arg) {|ary| ... } => nil
+ *     enum.gather_each(arg)              => enumerator
+ *
+ *  Iterates for each gathered elements of _enum_.
+ *
+ *  This method gathers consecutive elements which
+ *  _arg_.call(_element_) returns a same value.
+ *
+ *  The following values has special meaning:
+ *  - nil, false and :reject specifies that gathered elements is not yielded.
+ *  - :singleton specifies the element should be gathered only itself.
+ *
+ *  The gathered element is yielded as an array.
+ *
+ *  If the block is not given, an enumerator is returned.
+ *
+ *    (1..10).gather_each(lambda {|n| n & 2 }) {|a| p a }
+ *    #=> [1]           # 1 & 2 = 0
+ *    #   [2, 3]        # 2 & 2 = 3 & 2 = 1
+ *    #   [4, 5]        # 4 & 2 = 5 & 2 = 0
+ *    #   [6, 7]        # 6 & 2 = 7 & 2 = 1
+ *    #   [8, 9]        # 8 & 2 = 9 & 2 = 0
+ *    #   [10]          # 10 & 2 = 1
+ *
+ *    # gather indented blocks.
+ *    io.gather_each(lambda {|line| /\A\s/ =~ line }) {|lines| pp lines }
+ *
+ */
+static VALUE
+enum_gather_each(int argc, VALUE *argv, VALUE self)
+{
+    struct gather_each_st state;
+
+    rb_scan_args(argc, argv, "01", &state.arg);
+
+    RETURN_ENUMERATOR(self, argc, argv);
+
+    state.prev_value = Qnil;
+    state.prev_elts = Qnil;
+
+    rb_block_call(self, id_each, 0, 0, gather_each_i, (VALUE)&state);
+
+    if (state.prev_value != Qnil)
+        rb_yield(state.prev_elts);
+
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     enum.gather {|elt| ... } => enumerator
+ *
+ *  Creates an enumerator for iterating gathered elements of _enum_.
+ *
+ *  This method gathers consecutive elements which
+ *  the blocks returns a same value.
+ *
+ *  The following values has special meaning:
+ *  - nil, false and :reject specifies that gathered elements is not yielded.
+ *  - :singleton specifies the element should be gathered only itself.
+ *
+ *    (1..10).gather {|n| n & 2 }.each {|a| p a }
+ *    #=> [1]           # 1 & 2 = 0
+ *    #   [2, 3]        # 2 & 2 = 3 & 2 = 1
+ *    #   [4, 5]        # 4 & 2 = 5 & 2 = 0
+ *    #   [6, 7]        # 6 & 2 = 7 & 2 = 1
+ *    #   [8, 9]        # 8 & 2 = 9 & 2 = 0
+ *    #   [10]          # 10 & 2 = 1
+ *
+ *    # gather indented blocks.
+ *    io.gather {|line| /\A\s/ =~ line }.each {|lines| pp lines }
+ *
+ */
+static VALUE
+enum_gather(VALUE self)
+{
+    VALUE block = rb_block_proc();
+    return rb_enumeratorize(self, ID2SYM(rb_intern("gather_each")), 1, &block);
+}
+
 /*
  *  The <code>Enumerable</code> mixin provides collection classes with
  *  several traversal and searching methods, and with the ability to
@@ -1852,6 +1987,8 @@ Init_Enumerable(void)
     rb_define_method(rb_mEnumerable, "drop", enum_drop, 1);
     rb_define_method(rb_mEnumerable, "drop_while", enum_drop_while, 0);
     rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1);
+    rb_define_method(rb_mEnumerable, "gather_each", enum_gather_each, -1);
+    rb_define_method(rb_mEnumerable, "gather", enum_gather, 0);
 
     id_eqq  = rb_intern("===");
     id_each = rb_intern("each");
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread