camel

今のはまずかったよ、ひさいち。

遅いeachの代わりに使うspliceのメリット、デメリット | パルカワ!
で、今回は遅いeachを使う代わりに高速なspliceはどうでしょう?って話。

よりにもよって、初期化を放り投げるなんて!

# …
my %mapping = my @mapping = map {$_ => $_} (1..100); # ここで初期化しているけど…

cmpthese -1, {
    splice => sub {
        while (my ($key, $value) = splice @mapping, 0, 2) {}
    },
    # …
};
use strict;
use warnings;
use Benchmark qw(:all);

print "splice(), each(), keys(), values() Benchmark!!\n";

cmpthese -1, {
    splice => sub {
        my @mapping = %hash; # each()と等価にするためには、こうしないと
        while (my ($key, $value) = splice @mapping, 0, 2) {}
    },
    # …
};

普段は肌身離さず初期化しているんだから、こういう事故は滅多にあることじゃないんだけど。

あと、元のコード、

each()は遅い上に微妙な問題も起きやすい - Islands in the byte stream
cmpthese -1, {
    # …
    keys => sub {
        foreach my $key(keys %hash) { } # これも each と等価じゃないよね?
    },
    # …
};
cmpthese -1, {
    # …
    keys => sub {
        # 等価にするなら、こう。
        foreach my $key ( keys %hash ) { my $value = $hash{$key}; }
    },
    # …
};

で、上記を反映させた結果が、こちら。

          Rate  splice each_kv  each_k     map    keys  values
splice   135/s      --    -41%    -63%    -65%    -66%    -91%
each_kv  226/s     68%      --    -37%    -41%    -42%    -85%
each_k   359/s    167%     59%      --     -7%     -9%    -76%
map      384/s    186%     70%      7%      --     -2%    -75%
keys     393/s    192%     74%      9%      2%      --    -74%
values  1520/s   1030%    571%    323%    295%    287%      --

splice、keysどころかeachにも及ばないわ。

というわけで、hashをより効率良く運用できる、コンパクトで安全な構文はこう。

for my $key (keys %hash) {
    my $value = $hash{$key};
    # and use $key and $value here
}

実際、ハッシュをイテレートする際にはsortと組み合わせることも多いので、使いやすくもある。

for my $key (sort keys %hash) {
    my $value = $hash{$key};
    # and use $key and $value here
}

とはいえ、%hash$keyを二度書かないといけないところがイケてないとは私も思う。hash.each{ |k,v| #… } と素直に書けるRubyistsの「わけがわからないよ」という声が聞こえてきそうだ…

Dan the Perl Monger

#!perl
use strict;
use warnings;
use Benchmark qw(:all);

my %hash = map { $_ => $_ } ( 1 .. 10000 );

cmpthese timethese - 1, {
    each_k => sub {
        while ( my $key = each %hash ) { }
    },
    each_kv => sub {
        while ( my ( $key, $value ) = each %hash ) { }
    },
    keys => sub {
        foreach my $key ( keys %hash ) { my $value = $_; }
    },
    values => sub {
        foreach my $value ( values %hash ) { }
    },
    map => sub {
        map { my $key = $hash{$_} } keys %hash;
    },
    splice => sub {
        my @keys = %hash;
        while ( my ( $key, $value ) = splice @keys, 0, 2 ) { }
    },
};