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

ruby-changes:9094

From: knu <ko1@a...>
Date: Thu, 11 Dec 2008 19:40:43 +0900 (JST)
Subject: [ruby-changes:9094] Ruby:r20631 (ruby_1_8): * enumerator.c (enumerator_initialize): Add an ability to generate

knu	2008-12-11 19:39:31 +0900 (Thu, 11 Dec 2008)

  New Revision: 20631

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

  Log:
    * enumerator.c (enumerator_initialize): Add an ability to generate
      an enumerator from a block. [ruby-dev:35903]

  Modified files:
    branches/ruby_1_8/ChangeLog
    branches/ruby_1_8/NEWS
    branches/ruby_1_8/enumerator.c

Index: ruby_1_8/NEWS
===================================================================
--- ruby_1_8/NEWS	(revision 20630)
+++ ruby_1_8/NEWS	(revision 20631)
@@ -27,12 +27,14 @@
     New method with which replaces #choice.
 
   * Enumerable#each_with_object
+  * Enumerator#with_object
 
-    New method.
+    New methods.
 
-  * Enumerator#with_object
+  * Enumerator.new { |y| ... }
 
-    New method.
+    Now can generate an enumerator from a block that defines
+    enumeration instead of an enumerable object.
 
   * Hash#default_proc=
 
Index: ruby_1_8/ChangeLog
===================================================================
--- ruby_1_8/ChangeLog	(revision 20630)
+++ ruby_1_8/ChangeLog	(revision 20631)
@@ -1,3 +1,8 @@
+Thu Dec 11 19:33:10 2008  Akinori MUSHA  <knu@i...>
+
+	* enumerator.c (enumerator_initialize): Add an ability to generate
+	  an enumerator from a block. [ruby-dev:35903]
+
 Thu Dec 11 19:08:38 2008  Akinori MUSHA  <knu@i...>
 
 	* enumerator.c (enum_each_with_object): Add
Index: ruby_1_8/enumerator.c
===================================================================
--- ruby_1_8/enumerator.c	(revision 20630)
+++ ruby_1_8/enumerator.c	(revision 20631)
@@ -33,7 +33,25 @@
     rb_block_call_func *iter;
 };
 
+static VALUE rb_cGenerator, rb_cYielder;
+
+struct generator {
+    VALUE proc;
+};
+
+struct yielder {
+    VALUE proc;
+};
+
+static VALUE generator_allocate _((VALUE klass));
+static VALUE generator_init _((VALUE obj, VALUE proc));
+
+/*
+ * Enumerator
+ */
+
 static void enumerator_mark _((void *));
+
 static void
 enumerator_mark(p)
     void *p;
@@ -292,12 +310,34 @@
 /*
  *  call-seq:
  *    Enumerator.new(obj, method = :each, *args)
+ *    Enumerator.new { |y| ... }
  *
  *  Creates a new Enumerator object, which is to be used as an
- *  Enumerable object using the given object's given method with the
- *  given arguments.
+ *  Enumerable object iterating in a given way.
  *
- *  Use of this method is discouraged.  Use Kernel#enum_for() instead.
+ *  In the first form, a generated Enumerator iterates over the given
+ *  object using the given method with the given arguments passed.
+ *  Use of this form is discouraged.  Use Kernel#enum_for(), alias
+ *  to_enum, instead.
+ *
+ *    e = Enumerator.new(ObjectSpace, :each_object)
+ *        #-> ObjectSpace.enum_for(:each_object)
+ *
+ *    e.select { |obj| obj.is_a?(Class) }  #=> array of all classes
+ *
+ *  In the second form, iteration is defined by the given block, in
+ *  which a "yielder" object given as block parameter can be used to
+ *  yield a value by calling the +yield+ method, alias +<<+.
+ *
+ *    fib = Enumerator.new { |y|
+ *      a = b = 1
+ *      loop {
+ *        y << a
+ *        a, b = b, a + b
+ *      }
+ *    }
+ *
+ *    p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  */
 static VALUE
 enumerator_initialize(argc, argv, obj)
@@ -307,13 +347,19 @@
 {
     VALUE recv, meth = sym_each;
 
-    if (argc == 0)
-	rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
-    recv = *argv++;
-    if (--argc) {
-	meth = *argv++;
-	--argc;
+    if (argc == 0) {
+	if (!rb_block_given_p())
+	    rb_raise(rb_eArgError, "wrong number of argument (0 for 1+)");
+
+	recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc());
+    } else {
+	recv = *argv++;
+	if (--argc) {
+	    meth = *argv++;
+	    --argc;
+	}
     }
+
     return enumerator_init(obj, recv, meth, argc, argv);
 }
 
@@ -490,6 +536,242 @@
     return rb_funcall(obj, rb_intern("rewind"), 0, 0);
 }
 
+/*
+ * Yielder
+ */
+
+static void yielder_mark _((void *));
+
+static void
+yielder_mark(void *p)
+{
+    struct yielder *ptr = p;
+    rb_gc_mark(ptr->proc);
+}
+
+static struct yielder *
+yielder_ptr(VALUE obj)
+{
+    struct yielder *ptr;
+
+    Data_Get_Struct(obj, struct yielder, ptr);
+    if (RDATA(obj)->dmark != yielder_mark) {
+	rb_raise(rb_eTypeError,
+		 "wrong argument type %s (expected %s)",
+		 rb_obj_classname(obj), rb_class2name(rb_cYielder));
+    }
+    if (!ptr || ptr->proc == Qundef) {
+	rb_raise(rb_eArgError, "uninitialized yielder");
+    }
+    return ptr;
+}
+
+static VALUE yielder_allocate _((VALUE));
+
+/* :nodoc: */
+static VALUE
+yielder_allocate(klass)
+    VALUE klass;
+{
+    struct yielder *ptr;
+    VALUE obj;
+
+    obj = Data_Make_Struct(klass, struct yielder, yielder_mark, -1, ptr);
+    ptr->proc = Qundef;
+
+    return obj;
+}
+
+static VALUE
+yielder_init(obj, proc)
+    VALUE obj, proc;
+{
+    struct yielder *ptr;
+
+    Data_Get_Struct(obj, struct yielder, ptr);
+
+    if (!ptr) {
+	rb_raise(rb_eArgError, "unallocated yielder");
+    }
+
+    ptr->proc = proc;
+
+    return obj;
+}
+
+/* :nodoc: */
+static VALUE
+yielder_initialize(obj)
+    VALUE obj;
+{
+    rb_need_block();
+
+    return yielder_init(obj, rb_block_proc());
+}
+
+/* :nodoc: */
+static VALUE
+yielder_yield(obj, args)
+    VALUE obj, args;
+{
+    struct yielder *ptr = yielder_ptr(obj);
+
+    rb_proc_call(ptr->proc, args);
+
+    return obj;
+}
+
+static VALUE
+yielder_new_i(dummy)
+    VALUE dummy;
+{
+    return yielder_init(yielder_allocate(rb_cYielder), rb_block_proc());
+}
+
+static VALUE
+yielder_yield_i(obj, memo)
+    VALUE obj, memo;
+{
+    return rb_yield(obj);
+}
+
+static VALUE
+yielder_new()
+{
+    return rb_iterate(yielder_new_i, (VALUE)0, yielder_yield_i, (VALUE)0);
+}
+
+/*
+ * Generator
+ */
+
+static void generator_mark _((void *));
+
+static void
+generator_mark(p)
+    void *p;
+{
+    struct generator *ptr = p;
+    rb_gc_mark(ptr->proc);
+}
+
+static struct generator *
+generator_ptr(obj)
+    VALUE obj;
+{
+    struct generator *ptr;
+
+    Data_Get_Struct(obj, struct generator, ptr);
+    if (RDATA(obj)->dmark != generator_mark) {
+	rb_raise(rb_eTypeError,
+		 "wrong argument type %s (expected %s)",
+		 rb_obj_classname(obj), rb_class2name(rb_cGenerator));
+    }
+    if (!ptr || ptr->proc == Qundef) {
+	rb_raise(rb_eArgError, "uninitialized generator");
+    }
+    return ptr;
+}
+
+static VALUE generator_allocate _((VALUE));
+
+/* :nodoc: */
+static VALUE
+generator_allocate(klass)
+    VALUE klass;
+{
+    struct generator *ptr;
+    VALUE obj;
+
+    obj = Data_Make_Struct(klass, struct generator, generator_mark, -1, ptr);
+    ptr->proc = Qundef;
+
+    return obj;
+}
+
+static VALUE
+generator_init(obj, proc)
+    VALUE obj, proc;
+{
+    struct generator *ptr;
+
+    Data_Get_Struct(obj, struct generator, ptr);
+
+    if (!ptr) {
+	rb_raise(rb_eArgError, "unallocated generator");
+    }
+
+    ptr->proc = proc;
+
+    return obj;
+}
+
+VALUE rb_obj_is_proc _((VALUE));
+
+/* :nodoc: */
+static VALUE
+generator_initialize(argc, argv, obj)
+    int argc;
+    VALUE *argv;
+    VALUE obj;
+{
+    VALUE proc;
+
+    if (argc == 0) {
+	rb_need_block();
+
+	proc = rb_block_proc();
+    } else {
+	rb_scan_args(argc, argv, "1", &proc);
+
+	if (!rb_obj_is_proc(proc))
+	    rb_raise(rb_eTypeError,
+		     "wrong argument type %s (expected Proc)",
+		     rb_obj_classname(proc));
+
+	if (rb_block_given_p()) {
+	    rb_warn("given block not used");
+	}
+    }
+
+    return generator_init(obj, proc);
+}
+
+/* :nodoc: */
+static VALUE
+generator_init_copy(obj, orig)
+    VALUE obj, orig;
+{
+    struct generator *ptr0, *ptr1;
+
+    ptr0 = generator_ptr(orig);
+
+    Data_Get_Struct(obj, struct generator, ptr1);
+
+    if (!ptr1) {
+	rb_raise(rb_eArgError, "unallocated generator");
+    }
+
+    ptr1->proc = ptr0->proc;
+
+    return obj;
+}
+
+/* :nodoc: */
+static VALUE
+generator_each(obj)
+    VALUE obj;
+{
+    struct generator *ptr = generator_ptr(obj);
+    VALUE yielder;
+
+    yielder = yielder_new();
+
+    rb_proc_call(ptr->proc, rb_ary_new3(1, yielder));
+
+    return obj;
+}
+
 void
 Init_Enumerator()
 {
@@ -518,6 +800,21 @@
 
     rb_eStopIteration   = rb_define_class("StopIteration", rb_eIndexError);
 
+    /* Generator */
+    rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject);
+    rb_include_module(rb_cGenerator, rb_mEnumerable);
+    rb_define_alloc_func(rb_cGenerator, generator_allocate);
+    rb_define_method(rb_cGenerator, "initialize", generator_initialize, -1);
+    rb_define_method(rb_cGenerator, "initialize_copy", generator_init_copy, 1);
+    rb_define_method(rb_cGenerator, "each", generator_each, 0);
+
+    /* Yielder */
+    rb_cYielder = rb_define_class_under(rb_cEnumerator, "Yielder", rb_cObject);
+    rb_define_alloc_func(rb_cYielder, yielder_allocate);
+    rb_define_method(rb_cYielder, "initialize", yielder_initialize, 0);
+    rb_define_method(rb_cYielder, "yield", yielder_yield, -2);
+    rb_define_method(rb_cYielder, "<<", yielder_yield, -2);
+
     sym_each	 	= ID2SYM(rb_intern("each"));
 
     /* backward compatibility */

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

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