Perlを書いている時, 「いずれかと一致するなら...」みたいな条件を書く時に, List::Utilのany
を使うことがある.
例えば, @ids
という変数に何かしらのIDの一覧があって, あるIDが$id
として渡ってきた時に, $id
が@ids
に含まれているかどうか, みたいなのは, any
を使って次のように書ける:
if (any { $_ eq $id ) @ids) { ... }
但し, このコードは@ids
に含まれる要素が増えれば増えるほど処理時間が伸びるはず. なので, こういう場合はhashを使って判定する方が早そうに思える:
my $hash = { $_ => 1 } @ids; if ($hash{$id}) { ... }
...というのをBenchmarkで確認してみたので, 忘備録としてメモっておきます.
use strict; use warnings; use List::Util qw(any); use Benchmark qw(:all); my $data = [1..10]; my $first = $data->[0]; my $last = $data->[-1]; my %prepared_hash = (map { $_ => 1 } $data->@*); timethese(100_000_000, { prepared_hash => sub { $prepared_hash{$last}; }, hash => sub { my %hash = (map { $_ => 1 } $data->@*); $hash{$last}; }, any_first => sub { any { $_ eq $first } $data->@*; }, any_last => sub { any { $_ eq $last } $data->@*; }, });
prepared_hash
はhashを事前に準備するパターン. hash
は都度hashを生成するパターンで, any_first
とany_last
は先頭/末尾との一致をany
で判定するパターン. any
を使うパターンでも, any_last
よりもany_first
の方が先に処理が終わる(1回目でany
の条件部が真になるので, そこで処理が打ち切られる)はず. 果たして結果は...
Benchmark: timing 100000000 iterations of any_first, any_last, hash, prepared_hash... any_first: 24 wallclock secs (24.48 usr + 0.14 sys = 24.62 CPU) @ 4061738.42/s (n=100000000) any_last: 44 wallclock secs (43.14 usr + 0.22 sys = 43.36 CPU) @ 2306273.06/s (n=100000000) hash: 112 wallclock secs (110.83 usr + 0.50 sys = 111.33 CPU) @ 898230.49/s (n=100000000) prepared_hash: 3 wallclock secs ( 2.40 usr + 0.00 sys = 2.40 CPU) @ 41666666.67/s (n=100000000)
結果としては, hashを事前に準備するパターン > any
を使うパターン > hashを都度準備するパターン, となりました.
なので, @ids
に該当する部分が不変で, 判定が何度も呼び出される場合は, any
を使わずに事前にhashを用意してあげた方が素早く処理できそうですね.
最近のPerlなら, こういう感じでstate
を使ってあげる手もありそうです:
state %hash = map { $_ => 1 } @ids; if ($hash{$id}) { ... }