Re2: Enumerable#mins_by

nowa サービス終了のお知らせ
Re: Enumerable#mins_by - チナミニ
反則?

//minsby.c

#include <ruby.h>

static ID id_cmp, id_each;

static VALUE
mins_by_i(VALUE i, VALUE *memo, int argc, VALUE *argv)
{
    VALUE v;

    //ENUM_WANT_SVALUE();
    if(argc == 0) {
        i = Qnil;
    } else if(argc == 1) {
        i = argv[0];
    } else if(argc > 0) {
        i = rb_ary_new4(argc, argv);
    }

    v = rb_yield(i);
    if (memo[0] == Qundef) {
        memo[0] = v;
        rb_ary_push(memo[1], i);
    }
    else {
        int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, memo[0]), v, memo[0]);
        if (cmp < 0) {
            memo[0] = v;
            rb_ary_clear(memo[1]);
            rb_ary_push(memo[1], i);
        } else if (cmp == 0) {
            rb_ary_push(memo[1], i);
        }
    }
    return Qnil;
}

static VALUE
enum_mins_by(VALUE obj)
{
    VALUE memo[2];

    RETURN_ENUMERATOR(obj, 0, 0);

    memo[0] = Qundef;
    memo[1] = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, mins_by_i, (VALUE)memo);
    return memo[1];
}

void Init_minsby()
{
    rb_define_method(rb_mEnumerable, "c_mins_by", enum_mins_by, 0);
    id_cmp = rb_intern("<=>");
    id_each = rb_intern("each");
}
Rehearsal --------------------------------------
my   0.010000   0.000000   0.010000 (  0.005119)
uj   0.000000   0.000000   0.000000 (  0.004347)
c    0.000000   0.000000   0.000000 (  0.004221)
----------------------------- total: 0.010000sec

         user     system      total        real
my   0.000000   0.010000   0.010000 (  0.004150)
uj   0.010000   0.000000   0.010000 (  0.004085)
c    0.000000   0.000000   0.000000 (  0.002646)

なお、"my"は検定中の要素に負数があるときバグりますが、"c"はバグりません。

myはselect_byのようなものを使って書きなおせればうまくいくはず。