From: Yusuke ENDOH Date: 2009-02-05T23:18:49+09:00 Subject: [ruby-dev:37921] [Feature:trunk] with_index_from 遠藤です。 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 n. 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 block with two arguments, the item and its index, + * for each item in enum. The index starts from n. + * 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