[Feature:trunk] with_index_from


#1

e$B1sF#$G$9!#e(B

with_index
e$B$N3+;O%$%s%G%C%/%9$r;XDj$G$-$k%P!<%8%g%s$,$"$k$HJXMx$@$He(B
e$B;W$$$^$9!#e(B

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

e$B%$%s%G%C%/%9$r;XDj$7$?$$M}M3$H$7$F$O!"e(B

  • e$B%f!<%6%$%s%?!<%U%’%$%9$G$Oe(B 1 e$BHVL$+$i?t$($k$3$H$,B?$$e(B
    (e$B;2>He(B) http://d.hatena.ne.jp/rubikitch/20080524/1211627661

  • e$BNs$d9TNs$Ne(B 0 e$BHVL$re(B (e$B=i4|CM$J$I$H$7$Fe(B)
    e$BFCJL07$$$9$k%"%k%4%j%:%`$,$"$ke(B
    (e$B;2>He(B) http://d.hatena.ne.jp/ku-ma-me/20081026/p2

  • e$B@hF,$rHt$P$7$F%$%s%G%C%/%9IU$-%$%F%l!<%H$9$k$N$,4JC1$K$J$ke(B
    (e$BNce(B) ary.each_with_index {|x, i| next if i == 0; … } e$B$de(B
    ary.drop(1).each_with_index {|x, i| i += 1; … } e$B$,e(B
    ary.drop(1).each_with_index_from(1) {|x, y| … }
    e$B$K$J$C$F$9$C$-$je(B

e$B$J$I$,$"$j$^$9!#e(B

e$B0J2<$N%Q%C%A$Oe(B 3 e$B$D$N%a%=%C%I$rDI2C$7$^$9!#e(B

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

e$B$I$&$G$7$g$&$+!#e(B

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);

#2

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 ã¨ã„ã†åå‰ã‹ã‚‰ä»Šå¾Œå¢—åˆ†ç­‰ã‚’è¿½åŠ ã™ã‚‹ã“ã¨ã¯è€ƒãˆã«ãã„
ので、唯一の引数が初期値(オフセット)というのは納得できるの
ではないかと思いますが、どうでしょう。


#3

e$B1sF#$G$9!#e(B

2009/02/06 0:43 Akinori MUSHA removed_email_address@domain.invalid:

(each_)with_index e$B$K>JN,2DG=0z?t$rDI2C$9$k$N$O$I$&$G$7$g$&!#e(B

   with_index(offset = 0)

index e$B$H$$$&L>A0$+$i:#8eA}J,Ey$rDI2C$9$k$3$H$O9M$($K$/$$e(B
e$B$N$G!“M#0l$N0z?t$,=i4|CMe(B(e$B%*%U%;%C%He(B)e$B$H$$$&$N$OG<F@$G$-$k$Ne(B
e$B$G$O$J$$$+$H;W$$$^$9$,!”$I$&$G$7$g$&!#e(B

with_index e$B$O$=$l$GLdBj$J$$$s$G$9$,!"0d48$J$,$ie(B each_with_index
e$B$O0z?t$re(B
each
e$B$K4]Ej$2$9$k$H8@$&;EMM$K$J$C$F$$$k$N$G!“8_49@-$NLdBj$,$”$j$^$9!#e(B

$ ruby19 -rstringio -e
‘StringIO.new(“foo|bar|baz”).each_with_index("|") {|s, i| p [s, i] }’
[“foo|”, 0]
[“bar|”, 1]
[“baz”, 2]

e$B$I$&$9$k$N$,0lHV9,$;$G$7$g$&$M!#e(B


#4

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:37928] Re: [Feature:trunk] with_index_from”
on Fri, 6 Feb 2009 12:04:30 +0900, “Akinori MUSHA”
removed_email_address@domain.invalid writes:

|e$B!!$7$+$7!"e(B each_with_index_from e$B$H$$$&L>A0$OD9$9$.$k$H;W$$$^$9!#e(B
|e$B$^$?e(B each_with_index e$B<+BN!“e(B Enumerator e$B$,F3F~$5$l$?:#$H$J$C$F$Oe(B
|each.with_index e$B$h$j$b<c43<B9T8zN($,$h$$$3$H$r=|$1$PB8:_0U5A$,e(B
|e$BGv$$$G$9$M!#e(B
|
|e$B!!$3$3$O!“e(B with_index e$B$K$@$10z?t$rDI2C$7$Fe(B each_with_index e$B$Oe(B
|e$B$=$N$^$^$K$7$F$O$I$&$G$7$g$&!#e(B
|
|StringIO.new(“foo|bar|baz”).each(”|”).with_index(1) { … }
|
|e$B!!$A$g$C$H6E$C$?$3$H$r$7$?$1$l$Pe(B Enumerator e$B7PM3$G9T$C$F$M!"e(B
|e$B$H$$$&%9%?%s%9$O0-$/$O$J$$$H;W$$$^$9!#e(B

each_with_indexe$B$OJ|CV!#e(BEnumerator#with_indexe$B$NJ}$G<jEv$F$9$ke(B
e$BJ}$K;?@.$7$^$9!#$,!"8=;~E@$G$O4J7i$Je(Bwith_index(1)e$B$K$9$k$N$+!"e(B
e$B$h$jL@3N$Je(Bwith_index_from(1)e$B$K$9$k$N$+$O7hCG$G$-$^$;$s!#$I$Ce(B
e$B$A$,$h$$$G$7$g$&$M$(!#e(B


#5

At Fri, 6 Feb 2009 00:58:59 +0900,
Yusuke ENDOH wrote:

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]

どうするのが一番幸せでしょうね。

 ああ、その仕様は忘れていました。

 しかし、 each_with_index_from という名前は長すぎると思います。
また each_with_index 自体、 Enumerator が導入された今となっては
each.with_index よりも若干実行効率がよいことを除けば存在意義が
薄いですね。

 ここは、 with_index ã«ã ã‘å¼•æ•°ã‚’è¿½åŠ ã—ã¦ each_with_index は
そのままにしてはどうでしょう。

StringIO.new(“foo|bar|baz”).each("|").with_index(1) { … }

 ちょっと凝ったことをしたければ Enumerator 経由で行ってね、
というスタンスは悪くはないと思います。


#6

e$B1sF#$G$9!#e(B

2009/2/6 Yukihiro M. removed_email_address@domain.invalid:

|e$B!!$3$3$O!“e(B with_index e$B$K$@$10z?t$rDI2C$7$Fe(B each_with_index e$B$Oe(B
|e$B$=$N$^$^$K$7$F$O$I$&$G$7$g$&!#e(B
|
|StringIO.new(“foo|bar|baz”).each(”|").with_index(1) { … }
|
|e$B!!$A$g$C$H6E$C$?$3$H$r$7$?$1$l$Pe(B Enumerator e$B7PM3$G9T$C$F$M!"e(B
|e$B$H$$$&%9%?%s%9$O0-$/$O$J$$$H;W$$$^$9!#e(B

e$B$J$k$[$I!*e(B e$B46I~$7$^$7$?!#e(B

each_with_indexe$B$OJ|CV!#e(BEnumerator#with_indexe$B$NJ}$G<jEv$F$9$ke(B
e$BJ}$K;?@.$7$^$9!#$,!"8=;~E@$G$O4J7i$Je(Bwith_index(1)e$B$K$9$k$N$+!"e(B
e$B$h$jL@3N$Je(Bwith_index_from(1)e$B$K$9$k$N$+$O7hCG$G$-$^$;$s!#$I$Ce(B
e$B$A$,$h$$$G$7$g$&$M$(!#e(B

with_index_from e$B$O$d$`$J$/9M$($?$b$N$J$N$G!"e(Bwith_index
e$B$K$@$1e(B
e$B0z?t$r;}$?$;$F$$$$$J$i$=$C$A$K0lI<Ej$8$^$9!#e(B

with_index e$B$@$10z?t$r;}$?$;$k%Q%C%A$G$9!#e(B

Index: enumerator.c

— enumerator.c (revision 22101)
+++ enumerator.c (working copy)
@@ -406,32 +406,51 @@

/*

  • call-seq:
    • e.with_index {|(*args), idx| … }
    • e.with_index(offset = 0) {|(*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.
    • start from +offset+. If no block is given, returns an enumerator.
    */
    static VALUE
    -enumerator_with_index(VALUE obj)
    +enumerator_with_index(int argc, VALUE *argv, VALUE obj)
    {
    struct enumerator *e;
  • VALUE memo = 0;
  • int argc = 0;
  • VALUE *argv = 0;
  • VALUE memo;
  • RETURN_ENUMERATOR(obj, 0, 0);
  • rb_scan_args(argc, argv, “01”, &memo);
  • RETURN_ENUMERATOR(obj, argc, argv);
  • memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo);
    e = enumerator_ptr(obj);
    if (e->args) {
    argc = RARRAY_LEN(e->args);
    argv = RARRAY_PTR(e->args);
    }
  • else {
  • argc = 0;
  • argv = NULL;
  • }
    return rb_block_call(e->obj, e->meth, argc, argv,
    enumerator_with_index_i, (VALUE)&memo);
    }

+/*

    • call-seq:
    • e.each_with_index {|(*args), idx| … }
    • e.each_with_index
    • Same as Enumeartor#with_index, except each_with_index does not
    • receive an offset argument.
  • */
    static VALUE
    +enumerator_each_with_index(VALUE obj)
    +{
  • return enumerator_with_index(0, NULL, obj);
    +}

+static VALUE
enumerator_with_object_i(VALUE val, VALUE memo)
{
return rb_yield_values(2, val, memo);
@@ -841,9 +860,9 @@
rb_define_method(rb_cEnumerator, “initialize”,
enumerator_initialize, -1);
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”,
    enumerator_each_with_index, 0);
    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”,
    enumerator_with_index, -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);

#7

At Sat, 7 Feb 2009 00:49:59 +0900,
Yusuke ENDOH wrote:

2009/2/6 Yukihiro M. removed_email_address@domain.invalid:
(snip)

each_with_indexは放置。Enumerator#with_indexの方で手当てする
方に賛成します。が、現時点では簡潔なwith_index(1)にするのか、
より明確なwith_index_from(1)にするのかは決断できません。どっ
ちがよいでしょうねえ。

with_index_from はやむなく考えたものなので、with_index にだけ
引数を持たせていいならそっちに一票投じます。

 ではこれで行きましょうか。

  • Iterates the given block for each element with an index, which
    • start from 0. If no block is given, returns an enumerator.
    • start from +offset+. If no block is given, returns an enumerator.

 これは元のgrammoなんですが、 s/start/starts/ と直してください。

 これとは別件ですが、 yield args, i ではなく yield *args, i の
方がいいという意見があった気がしますがどうなんでしょうね、という
ところで [1,2,3].each.with_index.with_index {|x| … } がうまく
行かないことに気がついた…。


#8

e$B%A%1%C%He(B #1112 e$B$,99?7$5$l$^$7$?!#e(B (by Yusuke E.)

e$B%9%F!<%?%9e(B Opene$B$+$ie(BClosede$B$KJQ99e(B
e$B?JD=e(B % 0e$B$+$ie(B100e$B$KJQ99e(B

Applied in changeset r22134.

http://redmine.ruby-lang.org/issues/show/1112


#9

e$B%A%1%C%He(B #1119 e$B$,99?7$5$l$^$7$?!#e(B (by Nobuyoshi N.)

e$B%9%F!<%?%9e(B Opene$B$+$ie(BClosede$B$KJQ99e(B

separated from #1112 unintentionally.

http://redmine.ruby-lang.org/issues/show/1119