Enumerable#find_index vs. Array#index

 [ruby-talk:178495] が発端で Enumerable#find_index というのが
ä»¥å‰è¿½åŠ ã•ã‚Œã¾ã—ãŸãŒã€æ©Ÿèƒ½çš„ã«ã¯ Array#index のサブセットです。
(index はブロック評価、指定値との比較を両サポート)

 Array 以外では #index(value) に相当する機能がないので、
Enumerable#find_index(value) をサポートしてはどうでしょうか。
(そして Array では効率のため alias find_index index します)

Index: enum.c

— enum.c (revision 15954)
+++ enum.c (working copy)
@@ -186,8 +186,19 @@
}

static VALUE
-find_index_i(VALUE i, VALUE *memo, int argc, VALUE *argv)
+find_index_i(VALUE i, VALUE *memo)
{

  • if (rb_equal(i, memo[2])) {
  • memo[0] = UINT2NUM(memo[1]);
  • rb_iter_break();
  • }
  • memo[1]++;
  • return Qnil;
    +}

+static VALUE
+find_index_iter_i(VALUE i, VALUE *memo, int argc, VALUE *argv)
+{
if (RTEST(enum_yield(argc, argv))) {
memo[0] = UINT2NUM(memo[1]);
rb_iter_break();
@@ -198,30 +209,48 @@

/*

  • call-seq:
    • enum.find_index()   {| obj | block }  => int
      
    • enum.find_index(value)            => int or nil
      
    • enum.find_index {| obj | block }  => int or nil
      
    • Passes each entry in enum to block. Returns the
    • index for the first for which block is not
      false.
    • If no object matches, returns nil
    • Compares each entry in enum with value or passes
    • to block. Returns the index for the first for which the
    • evaluated value is non-false. If no object matches, returns
    • nil
    • (1..10).find_index  {|i| i % 5 == 0 and i % 7 == 0 }   #=> nil
      
    • (1..100).find_index {|i| i % 5 == 0 and i % 7 == 0 }   #=> 34
      
    • (1..100).find_index(50)                                #=> 49
      
    */

static VALUE
-enum_find_index(VALUE obj)
+enum_find_index(int argc, VALUE *argv, VALUE obj)
{

  • VALUE memo[2];
  • VALUE memo[3]; /* [return value, current index, condition value]
    */
  • rb_block_call_func *func;
  • RETURN_ENUMERATOR(obj, 0, 0);
  • memo[0] = Qundef;
  • memo[1] = 0;
  • rb_block_call(obj, id_each, 0, 0, find_index_i, (VALUE)memo);
  • if (memo[0] != Qundef) {
  • return memo[0];
  • if (argc == 0) {
  •    RETURN_ENUMERATOR(obj, 0, 0);
    
  •    memo[0] = Qnil;
    
  •    memo[1] = 0;
    
  •    memo[2] = Qundef;
    
  •    func = find_index_iter_i;
    
    }
  • return Qnil;
  • else {
  •    VALUE item;
    
  • if (rb_block_given_p()) {
  •  rb_warn("given block not used");
    
  • }
  • rb_scan_args(argc, argv, “1”, &item);
  •    memo[0] = Qnil;
    
  •    memo[1] = 0;
    
  •    memo[2] = item;
    
  •    func = find_index_i;
    
  • }
  • rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
  • return memo[0];
    }

static VALUE
@@ -1688,7 +1717,7 @@
rb_define_method(rb_mEnumerable,“count”, enum_count, -1);
rb_define_method(rb_mEnumerable,“find”, enum_find, -1);
rb_define_method(rb_mEnumerable,“detect”, enum_find, -1);

  • rb_define_method(rb_mEnumerable,“find_index”, enum_find_index, 0);
  • rb_define_method(rb_mEnumerable,“find_index”, enum_find_index, -1);
    rb_define_method(rb_mEnumerable,“find_all”, enum_find_all, 0);
    rb_define_method(rb_mEnumerable,“select”, enum_find_all, 0);
    rb_define_method(rb_mEnumerable,“reject”, enum_reject, 0);
    Index: array.c
    ===================================================================
    — array.c (revision 15954)
    +++ array.c (working copy)
    @@ -877,6 +877,8 @@
  • a.index("b")        #=> 1
    
  • a.index("z")        #=> nil
    
  • a.index{|x|x=="b"}  #=> 1
    
    • This is an alias of #find_index.
      */

static VALUE
@@ -3378,6 +3380,7 @@
rb_define_method(rb_cArray, “length”, rb_ary_length, 0);
rb_define_alias(rb_cArray, “size”, “length”);
rb_define_method(rb_cArray, “empty?”, rb_ary_empty_p, 0);

  • rb_define_method(rb_cArray, “find_index”, rb_ary_index, -1);
    rb_define_method(rb_cArray, “index”, rb_ary_index, -1);
    rb_define_method(rb_cArray, “rindex”, rb_ary_rindex, -1);
    rb_define_method(rb_cArray, “join”, rb_ary_join_m, -1);

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

In message “Re: [ruby-dev:34313] Enumerable#find_index vs. Array#index”
on Thu, 10 Apr 2008 23:48:54 +0900, “Akinori MUSHA”
[email protected] writes:

|e$B!!e(B[ruby-talk:178495] e$B$,H/C<$Ge(B Enumerable#find_index e$B$H$$$&$N$,e(B
|e$B0JA0DI2C$5$l$^$7$?$,!“5!G=E*$K$Oe(B Array#index e$B$N%5%V%;%C%H$G$9!#e(B
|(index e$B$O%V%m%C%/I>2A!”;XDjCM$H$NHf3S$rN>%5%]!<%He(B)
|
|e$B!!e(BArray e$B0J30$G$Oe(B #index(value) e$B$KAjEv$9$k5!G=$,$J$$$N$G!"e(B
|Enumerable#find_index(value) e$B$r%5%]!<%H$7$F$O$I$&$G$7$g$&$+!#e(B
|(e$B$=$7$Fe(B Array e$B$G$O8zN($N$?$ae(B alias find_index index e$B$7$^$9e(B)

e$B$U$`!#%3%_%C%H$7$F$/$@$5$$e(B(1.9e$B$Ke(B)e$B!#e(B1.8e$B$K$D$$$F$NH=CG$O$*G$$;e(B
e$B$7$^$9!#e(B

e$B$"$H!"e(BDavid Flanagane$B$,e(B[ruby-core:16331]e$B$G!"e(BObject.tap,
Proc.curry, Symbol.to_proce$B$H$+$rK\Ev$Ke(B1.8e$B$KF3F~$9$k$D$b$j$+e(B
e$BAa5^$KCN$j$?$$$H$$$C$F$^$9!#<!$NHG$N869F!:@Z$,:#=5Kv$J$N$@$=e(B
e$B$&$G$9!#e(B

At Fri, 11 Apr 2008 09:22:06 +0900,
matz wrote:

ふむ。コミットしてください(1.9に)。1.8についての判断はお任せ
します。

あと、David Flanaganが[ruby-core:16331]で、Object.tap,
Proc.curry, Symbol.to_procとかを本当に1.8に導入するつもりか
早急に知りたいといってます。次の版の原稿〆切が今週末なのだそ
うです。

 Object#tap と Symbol#to_proc は入れます。

 Proc#curry その他は余裕があったらですね。今週末、機能フリーズ
してpreview1を出します。

 西山さんは特に作業はしていないのかな。たくさん挙げてくれたけど、
私一人にできる量じゃないんですが。

 今のところ、新機能として入れるのが確定している(実際に作業中)
なのは Enumerable, Array, Hash の各メソッドです。

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

In message “Re: [ruby-dev:34327] Re: Enumerable#find_index vs.
Array#index”
on Fri, 11 Apr 2008 15:45:11 +0900, “Akinori MUSHA”
[email protected] writes:

|> e$B$"$H!"e(BDavid Flanagane$B$,e(B[ruby-core:16331]e$B$G!“e(BObject.tap,
|> Proc.curry, Symbol.to_proce$B$H$+$rK\Ev$Ke(B1.8e$B$KF3F~$9$k$D$b$j$+e(B
|> e$BAa5^$KCN$j$?$$$H$$$C$F$^$9!#<!$NHG$N869F!:@Z$,:#=5Kv$J$N$@$=e(B
|> e$B$&$G$9!#e(B
|
|e$B!!e(BObject#tap e$B$He(B Symbol#to_proc e$B$OF~$l$^$9!#e(B
|
|e$B!!e(BProc#curry e$B$=$NB>$OM>M5$,$”$C$?$i$G$9$M!#:#=5Kv!"5!G=%U%j!<%:e(B
|e$B$7$Fe(Bpreview1e$B$r=P$7$^$9!#e(B
|
|e$B!!@>;3$5$s$OFC$K:n6H$O$7$F$$$J$$$N$+$J!#$?$/$5$s5s$2$F$/$l$?$1$I!“e(B
|e$B;d0l?M$K$G$-$kNL$8$c$J$$$s$G$9$,!#e(B
|
|e$B!!:#$N$H$3$m!”?75!G=$H$7$FF~$l$k$N$,3NDj$7$F$$$ke(B(e$B<B:]$K:n6HCfe(B)
|e$B$J$N$Oe(B Enumerable, Array, Hash e$B$N3F%a%=%C%I$G$9!#e(B

e$B$b$7$h$1$l$Pe(Bruby-coree$B$Ge(BDavide$B$K%U%)%m!<$7$F$"$2$F$/$@$5$$!#e(B

e$B@>;3OB9-$G$9!#e(B

At Fri, 11 Apr 2008 19:23:23 +0900,
Kazuhiro NISHIYAMA wrote:

e$B$H$j$"$($:e(BApprovede$B$K$J$C$F$$$F4JC1$=$&$J$b$N$re(Bbackporte$B$7$F$_$F$$$^$9!#e(B
(Process.exec, Object#tap, Symbol#to_proce$B$J$Ie(B)

Symbol#to_proce$B$OC1=c$Ke(Btrunke$B$Ne(Bstring.ce$B$+$i$=$N$^$^e(Bruby_1_8e$B$Ne(Bobject.ce$B$Ke(B
e$BF~$l$F$b$&$^$/$$$+$J$+$C$?e(B(:to_s.to_proc.call(1)e$B$G$be(Bno
receiver givene$B$Ke(B
e$B$J$ke(B)e$B$N$G!"$"$-$i$a$^$7$?!#e(B

Index: object.c

— object.c (e$B%j%S%8%g%se(B 15977)
+++ object.c (e$B:n6H%3%T!<e(B)
@@ -1205,7 +1205,34 @@
return sym;
}

+static VALUE
+sym_call(VALUE args, VALUE sym, int argc, VALUE *argv)
+{

  • VALUE obj;

  • if (argc < 1) {

  • rb_raise(rb_eArgError, “no receiver given”);

  • }

  • obj = argv[0];

  • return rb_funcall(obj, (ID)sym, argc - 1, argv + 1);
    +}

+/*

    • call-seq:
    • sym.to_proc
    • Returns a Proc object which respond to the given method by sym.
    • (1…3).collect(&:to_s) #=> [“1”, “2”, “3”]
  • */

+static VALUE
+sym_to_proc(VALUE sym)
+{

  • return rb_proc_new(sym_call, (VALUE)SYM2ID(sym));
    +}

/***********************************************************************
*

  • Document-class: Module
    @@ -2749,6 +2776,7 @@
    rb_define_method(rb_cSymbol, “to_s”, sym_to_s, 0);
    rb_define_method(rb_cSymbol, “id2name”, sym_to_s, 0);
    rb_define_method(rb_cSymbol, “to_sym”, sym_to_sym, 0);
  • rb_define_method(rb_cSymbol, “to_proc”, sym_to_proc, 0);
    rb_define_method(rb_cSymbol, “===”, rb_obj_equal, 1);

    rb_define_method(rb_cModule, “freeze”, rb_mod_freeze, 0);
    Index: test/ruby/test_symbol.rb
    ===================================================================
    — test/ruby/test_symbol.rb (e$B%j%S%8%g%se(B 15977)
    +++ test/ruby/test_symbol.rb (e$B:n6H%3%T!<e(B)
    @@ -74,4 +74,8 @@
    assert_inspect_evaled(’:$0’)
    assert_inspect_evaled(’:$1’)
    end

  • def test_to_proc

  • assert_equal %w(1 2 3), (1…3).map(&:to_s)

  • end
    end

At Fri, 11 Apr 2008 19:45:35 +0900,
Kazuhiro NISHIYAMA wrote:

At Fri, 11 Apr 2008 19:23:23 +0900,
Kazuhiro NISHIYAMA wrote:

とりあえずApprovedになっていて簡単そうなものをbackportしてみています。
(Process.exec, Object#tap, Symbol#to_procなど)

Symbol#to_procは単純にtrunkのstring.cからそのままruby_1_8のobject.cに
入れてもうまくいかなかった(:to_s.to_proc.call(1)でもno receiver givenに
なる)ので、あきらめました。

 そのまま持って来られないものは多いです。

+static VALUE
+sym_call(VALUE args, VALUE sym, int argc, VALUE *argv)

 コア部分のソースなのでK&R形式にしてください。あと仮引数名の
sym は mid じゃないですかね。trunk がそうなのか。

 commit してください。(NEWS の更新も)

e$B@>;3OB9-$G$9!#e(B

At Fri, 11 Apr 2008 15:45:11 +0900,
Akinori MUSHA wrote:

e$B!!@>;3$5$s$OFC$K:n6H$O$7$F$$$J$$$N$+$J!#$?$/$5$s5s$2$F$/$l$?$1$I!"e(B
e$B;d0l?M$K$G$-$kNL$8$c$J$$$s$G$9$,!#e(B

e$B$H$j$"$($:e(BApprovede$B$K$J$C$F$$$F4JC1$=$&$J$b$N$re(Bbackporte$B$7$F$_$F$$$^$9!#e(B
(Process.exec, Object#tap, Symbol#to_proce$B$J$Ie(B)

Process.exece$B$Oe(Btrunke$B$NJ}$Ge(B
http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/process.c?r1=9571&r2=9744
e$B$N$h$&$KB>$NJQ99$H0l=o$Ke(Bcommite$B$5$l$F$$$?$N$G$9$,!"e(B
Process.exece$B$NDI2C$NJ}$@$1e(Bbackporte$B$7$^$7$?!#e(B