遠藤です。

with_index の開始インデックスを指定できるバージョンがあると便利だと
思います。


$ ./ruby -e 'p %w(a b c).map.with_index_from(1) {|c, i| [c, i] }'
[["a", 1], ["b", 2], ["c", 3]]


インデックスを指定したい理由としては、

- ユーザインターフェイスでは 1 番目から数えることが多い
  (参照) http://d.hatena.ne.jp/rubikitch/20080524/1211627661

- 列や行列の 0 番目を (初期値などとして) 特別扱いするアルゴリズムがある
  (参照) http://d.hatena.ne.jp/ku-ma-me/20081026/p2

- 先頭を飛ばしてインデックス付きイテレートするのが簡単になる
  (例) ary.each_with_index {|x, i| next if i == 0; ... } や
       ary.drop(1).each_with_index {|x, i| i += 1; ... } が
       ary.drop(1).each_with_index_from(1) {|x, y| ... } になってすっきり

などがあります。


以下のパッチは 3 つのメソッドを追加します。

  - Enumerator#each_with_index_from(n)
  - Enumerator#with_index_from(n)
  - Enumerable#each_with_index_from(n)

どうでしょうか。


Index: enumerator.c
===================================================================
--- enumerator.c	(revision 22080)
+++ enumerator.c	(working copy)
@@ -397,31 +397,20 @@
 }

 static VALUE
-enumerator_with_index_i(VALUE val, VALUE *memo)
+enumerator_with_index_i(VALUE val, long *memo)
 {
     val = rb_yield_values(2, val, INT2FIX(*memo));
     ++*memo;
     return val;
 }

-/*
- *  call-seq:
- *    e.with_index {|(*args), idx| ... }
- *    e.with_index
- *
- *  Iterates the given block for each element with an index, which
- *  start from 0.  If no block is given, returns an enumerator.
- *
- */
 static VALUE
-enumerator_with_index(VALUE obj)
+enumerator_with_index_core(VALUE obj, long memo)
 {
     struct enumerator *e;
-    VALUE memo = 0;
     int argc = 0;
     VALUE *argv = 0;

-    RETURN_ENUMERATOR(obj, 0, 0);
     e = enumerator_ptr(obj);
     if (e->args) {
 	argc = RARRAY_LEN(e->args);
@@ -431,7 +420,39 @@
 			 enumerator_with_index_i, (VALUE)&memo);
 }

+/*
+ *  call-seq:
+ *    e.with_index {|(*args), idx| ... }
+ *    e.with_index
+ *
+ *  Iterates the given block for each element with an index, which
+ *  starts from 0.  If no block is given, returns an enumerator.
+ *
+ */
 static VALUE
+enumerator_with_index(VALUE obj)
+{
+    RETURN_ENUMERATOR(obj, 0, 0);
+    return enumerator_with_index_core(obj, 0);
+}
+
+/*
+ *  call-seq:
+ *    e.with_index_from(n) {|(*args), idx| ... }
+ *    e.with_index_from(n)
+ *
+ *  Iterates the given block for each element with an index, which
+ *  starts from <i>n</i>.  If no block is given, returns an enumerator.
+ *
+ */
+static VALUE
+enumerator_with_index_from(VALUE obj, VALUE val)
+{
+    RETURN_ENUMERATOR(obj, 1, &val);
+    return enumerator_with_index_core(obj, NUM2LONG(val));
+}
+
+static VALUE
 enumerator_with_object_i(VALUE val, VALUE memo)
 {
     return rb_yield_values(2, val, memo);
@@ -842,8 +863,10 @@
     rb_define_method(rb_cEnumerator, "initialize_copy",
enumerator_init_copy, 1);
     rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
     rb_define_method(rb_cEnumerator, "each_with_index",
enumerator_with_index, 0);
+    rb_define_method(rb_cEnumerator, "each_with_index_from",
enumerator_with_index_from, 1);
     rb_define_method(rb_cEnumerator, "each_with_object",
enumerator_with_object, 1);
     rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
+    rb_define_method(rb_cEnumerator, "with_index_from",
enumerator_with_index_from, 1);
     rb_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1);
     rb_define_method(rb_cEnumerator, "next", enumerator_next, 0);
     rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
Index: enum.c
===================================================================
--- enum.c	(revision 22080)
+++ enum.c	(working copy)
@@ -1425,7 +1425,34 @@
     return obj;
 }

+/*
+ *  call-seq:
+ *     enum.each_with_index_from(n) {|obj, i| block }  -> enum
+ *
+ *  Calls <em>block</em> with two arguments, the item and its index,
+ *  for each item in <i>enum</i>.  The index starts from <i>n</i>.
+ *  Given arguments are passed through to #each().
+ *
+ *     hash = Hash.new
+ *     %w(cat dog wombat).each_with_index_from(1) {|item, index|
+ *       hash[item] = index
+ *     }
+ *     hash   #=> {"cat"=>1, "dog"=>2, "wombat"=>3}
+ *
+ */

+static VALUE
+enum_each_with_index_from(VALUE obj, VALUE val)
+{
+    long memo = NUM2LONG(val);
+
+    RETURN_ENUMERATOR(obj, 1, &val);
+
+    rb_block_call(obj, id_each, 0, 0, each_with_index_i, (VALUE)&memo);
+    return obj;
+}
+
+
 /*
  *  call-seq:
  *     enum.reverse_each {|item| block }
@@ -1845,6 +1872,7 @@
     rb_define_method(rb_mEnumerable, "member?", enum_member, 1);
     rb_define_method(rb_mEnumerable, "include?", enum_member, 1);
     rb_define_method(rb_mEnumerable, "each_with_index",
enum_each_with_index, -1);
+    rb_define_method(rb_mEnumerable, "each_with_index_from",
enum_each_with_index_from, 1);
     rb_define_method(rb_mEnumerable, "reverse_each", enum_reverse_each, -1);
     rb_define_method(rb_mEnumerable, "zip", enum_zip, -1);
     rb_define_method(rb_mEnumerable, "take", enum_take, 1);

-- 
Yusuke ENDOH <mame / tsg.ne.jp>