詳しくはCookbookの2.1を参照してもらうとして、これは以外とよく出てくる設問なので。
Kazuho@Cybozu Labs: MySQL の高速化プチBKところで、実数の場合はどうすればいいんでしょうね。
Scalar::Util::looks_like_number()
Perl 5.8.1以降であれば、第一選択はこれです。第一引数の結果が数値か否かを、Perl APIに直接問い合わせるので高速です。ベンチマークを最後に示しますが、真偽ともに正規表現と比較して250倍高速です。
Regexp::Commonの$RE{num}{real}
それ以外であれば、Regexp::CommonをCPANからインストールして、そこで定義される$RE{num}{real}を使うのがよいでしょう。
生の正規表現
これもCookbook(英語版2nd ed.)のp. 61に載っています。Perl以外でも使えるので、少し手直しした上で掲載。
sub isnt_a_number { $_[0] =~ /\D/ }
sub is_a_natual_number { $_[0] =~ /\A\d+\z/ }
sub is_an_integer { $_[0] =~ /\-?\d+\z/ }
sub is_an_integer2 { $_[0] =~ /\A[\+\-]?\d+\z/ } # +3
sub is_a_decimal_number { $_[0] =~ /\A[\+\-]?\d+\.?\d*\z/ }
sub is_a_decimal_number2 { $_[0] =~ /\A[\+\-]?(?:\d+(?:\.\d*)?|\.d+)\z/ }
sub is_a_C_float { $_[0] =~ /\A([\+\-])?(?=\d|\.d)\d*(\.\d*)?(([Ee])([\+\-]?\d+))?\z/ }
整数化して判断
実は、これが一番高速だったりします。
sub is_a_num { $_[0] != 0 }
ただし、この場合数値の0は数値と見なされません。0まできちんとチェックしたい場合は、
sub is_a_num { $_[0] != 0 or $_[0] =~ /\A0(?:\.0+)\z/ }
とすればよいでしょう。ただし、この場合"0 but true"のような場合など、NGのケースもあります。Scalar::Util::looks_like_number()が使える場合はそちらの方がよいでしょう。
Enjoy!
Dan the Number-Crunching Perl Monger
use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
use Regexp::Common;
use Benchmark qw/cmpthese timethese/;
my %posneg = (
pos => 6.0221415e23,
neg => "Avogadro's Number",
) ;
for my $pn ( keys %posneg ) {
my $su =
$pn eq 'pos'
? sub { looks_like_number( $posneg{$pn} ) or die }
: sub { looks_like_number( $posneg{$pn} ) and die };
my $rc =
$pn eq 'pos'
? sub { $posneg{$pn} =~ /\A$RE{num}{real}\z/ or die }
: sub { $posneg{$pn} =~ /\A$RE{num}{real}\z/ and die };
my $n0 = do {
no warnings 'numeric';
$pn eq 'pos'
? sub { $posneg{$pn} !=0 or die }
: sub { $posneg{$pn} !=0 and die };
};
my $n1 = do {
no warnings 'numeric';
$pn eq 'pos'
? sub {
( $posneg{$pn} != 0 or $posneg{$pn} =~ /\A0(?:\.0+)\z/ )
or die;
}
: sub {
( $posneg{$pn} != 0 or $posneg{$pn} =~ /\A0(?:\.0+)\z/ )
and die;
};
};
cmpthese(timethese(0,{
"S:U $pn" => $su,
"R:C $pn" => $rc,
"numify $pn" => $n0,
"num+rx $pn" => $n1,
}
));
}
Benchmark: running R:C pos, S:U pos, num+rx pos, numify pos for at least 3 CPU seconds...
R:C pos: 3 wallclock secs ( 3.21 usr + 0.01 sys = 3.22 CPU) @ 8217.39/s (n=26460)
S:U pos: 3 wallclock secs ( 3.05 usr + 0.02 sys = 3.07 CPU) @ 2277608.47/s (n=6992258)
num+rx pos: 3 wallclock secs ( 3.09 usr + 0.01 sys = 3.10 CPU) @ 4921243.87/s (n=15255856)
numify pos: 5 wallclock secs ( 3.07 usr + 0.02 sys = 3.09 CPU) @ 5526500.97/s (n=17076888)
Rate R:C pos S:U pos num+rx pos numify pos
R:C pos 8217/s -- -100% -100% -100%
S:U pos 2277608/s 27617% -- -54% -59%
num+rx pos 4921244/s 59788% 116% -- -11%
numify pos 5526501/s 67154% 143% 12% --
Benchmark: running R:C neg, S:U neg, num+rx neg, numify neg for at least 3 CPU seconds...
R:C neg: 3 wallclock secs ( 3.15 usr + 0.01 sys = 3.16 CPU) @ 8370.25/s (n=26450)
S:U neg: 4 wallclock secs ( 3.07 usr + 0.02 sys = 3.09 CPU) @ 2262869.90/s (n=6992268)
num+rx neg: 4 wallclock secs ( 3.15 usr + 0.01 sys = 3.16 CPU) @ 2541496.52/s (n=8031129)
numify neg: 0 wallclock secs ( 3.15 usr + 0.00 sys = 3.15 CPU) @ 6017865.08/s (n=18956275)
Rate R:C neg S:U neg num+rx neg numify neg
R:C neg 8370/s -- -100% -100% -100%
S:U neg 2262870/s 26935% -- -11% -62%
num+rx neg 2541497/s 30263% 12% -- -58%
numify neg 6017865/s 71796% 166% 137% --

このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。