Feature #1112
closedwith_index_from
Description
=begin
遠藤です。
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 mame@tsg.ne.jp
=end
Updated by mame (Yusuke Endoh) about 16 years ago
=begin
遠藤です。
2009/02/06 0:43 Akinori MUSHA knu@idaemons.org:
At Thu, 5 Feb 2009 23:18:49 +0900,
Yusuke ENDOH wrote:with_index の開始インデックスを指定できるバージョンがあると便利だと
思います。$ ./ruby -e 'p %w(a b c).map.with_index_from(1) {|c, i| [c, i] }'
[["a", 1], ["b", 2], ["c", 3]]新しいメソッドを追加するのは大仰な気もします。既存の
(each_)with_index に省略可能引数を追加するのはどうでしょう。with_index(offset = 0)
index という名前から今後増分等を追加することは考えにくい
ので、唯一の引数が初期値(オフセット)というのは納得できるの
ではないかと思いますが、どうでしょう。
with_index はそれで問題ないんですが、遺憾ながら each_with_index は引数を
each に丸投げすると言う仕様になっているので、互換性の問題があります。
$ ruby19 -rstringio -e
'StringIO.new("foo|bar|baz").each_with_index("|") {|s, i| p [s, i] }'
["foo|", 0]
["bar|", 1]
["baz", 2]
どうするのが一番幸せでしょうね。
--
Yusuke ENDOH mame@tsg.ne.jp
=end
Updated by mame (Yusuke Endoh) about 16 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
=begin
Applied in changeset r22134.
=end