Project

General

Profile

Actions

Feature #1112

closed

with_index_from

Added by mame (Yusuke Endoh) about 15 years ago. Updated almost 13 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-dev:37921]

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
=end

Actions #1

Updated by mame (Yusuke Endoh) about 15 years ago

=begin
遠藤です。

2009/02/06 0:43 Akinori MUSHA :

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

=end

Actions #2

Updated by mame (Yusuke Endoh) about 15 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
Applied in changeset r22134.
=end

Actions

Also available in: Atom PDF

Like0
Like0Like0