無気力生活 (ノ ´ω`)ノ ~゜

脱力系エンジニア。てきとーに生きてます。

Rubyにおいて要素有無判定にはcountよりempty?使おう、というゆるふわ話

弊社では担当プロダクトごとにエンジニアが複数人付く体制でWEBサービスの開発運用やってます。 その過程でコードレビューを実施してるわけなんですが、

とある日、他の人のコード見ていて要素があるかないか、をこう判定しているコードを見つけました。

## items: Array

return false if items.count == 0

countだと要素の数え上げしちゃうんでempty?使った方が効率いいっすよ、というレビューコメントを書いたわけですよ。 その時ふと、「本当にempty?のほうが良いのか?」という疑問が(;・`д・)

気になったので、少し深掘りしました。

countを使う場合

rubyソースコードが公開されてるので、それを見ます。 https://github.com/ruby/ruby/blob/trunk/array.c

rb_ary_countっていうものが該当します。

static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
    long n = 0;

    if (argc == 0) {
        VALUE *p, *pend;

        if (!rb_block_given_p())
            return LONG2NUM(RARRAY_LEN(ary));

        for (p = RARRAY_PTR(ary), pend = p + RARRAY_LEN(ary); p < pend; p++) {
            if (RTEST(rb_yield(*p))) n++;
        }
    }
    else {
        VALUE obj, *p, *pend;

        rb_scan_args(argc, argv, "1", &obj);
        if (rb_block_given_p()) {
            rb_warn("given block not used");
        }
        for (p = RARRAY_PTR(ary), pend = p + RARRAY_LEN(ary); p < pend; p++) {
            if (rb_equal(*p, obj)) n++;
        }
    }

    return LONG2NUM(n);
}

予想どおり、forを回してカウントとってますね。

empty?を使う場合。

一方empty?。これも同じソースコード中に定義が存在します。中身こんなんです。

static VALUE
rb_ary_empty_p(VALUE ary)
{
    if (RARRAY_LEN(ary) == 0)
        return Qtrue;
    return Qfalse;
}

RARRAY_LENしか読んでませんね。中身見るとこうなってます。

#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len

arrayが持っているlenというメンバに対して値を取得して判定してますね。 empty?では、要素数え上げしてないので、countより計算量は少ないわけですな。

まあ、この程度の数え上げする・しないの話は、全体の影響度からするとごくわずかな違いでしか無いので、普通にWebやってるだけなら気にする必要はないんですが、実際に蓋開けた結果を知っておくのも大事だと思いました。