詳しくは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% --
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。