in reply to Is A Number
The suggested Scalar::Util::looks_like_number() is often a good choice; however, be aware of some gotchas:
Infinity (see "perldata: Special floating point: infinity (Inf) and not-a-number (NaN)"):
perl -E ' use Scalar::Util "looks_like_number"; for (qw{Inf -Inf Infinity -Infinity}) { say "$_: ", looks_like_number($_) ? 1 : 0; } ' Inf: 1 -Inf: 1 Infinity: 1 -Infinity: 1
Non-decimal numbers:
perl -E ' use Scalar::Util "looks_like_number"; for (qw{1 0b10 0o10 0x10}) { say "$_: ", looks_like_number($_) ? 1 : 0 } ' 1: 1 0b10: 0 0o10: 0 0x10: 0
perl -E ' use Scalar::Util "looks_like_number"; for (1, 0b10, 0o10, 0x10) { say "$_: ", looks_like_number($_) ? 1 : 0 } ' 1: 1 2: 1 8: 1 16: 1
"I would like to combine the string comparisons into as few as possible but seem to break the code every time I try."
If looks_like_number() is causing you problems, perhaps due to "gotchas" indicated above, or as a purely academic exercise, here's some hints for a hand-crafted solution.
Get rid of the blacklist: this will take ages to get right and, even then, there's a high chance that you'll miss an edge-case or two (or more). Just use a whitelist which represents valid numbers for your application.
Use a single regex with alternations, and only define it once (not in every iteration). With Perl v5.10 or higher, you can use this format:
sub IsNumber { state $re = qr{...}; return $_[0] =~ $re; }
For Perls of an older vintage:
{ my $re; BEGIN { $re = qr{...} } sub IsNumber { return $_[0] =~ $re; } }
Don't try to write your regex in the smallest possible space. This isn't a golfing exercise. Readability and maintainability will very quickly deteriorate. Use the /x modifier — or /xx if available; requires Perl v5.26 or later — as explained in "perlre: /x and /xx".
Consider whether you're allowing exponents:
$ perl -E 'say 1e6' 1000000
What about when underscores have been used to improve the readability of large numbers:
$ perl -E 'say 1_000_000/1_000' 1000
Are you only dealing with 7-bit ASCII digits (/[0-9]/) or allowing the handling of all (i.e. Unicode®) digits (/\p{Digit}/)?
Note that /\d/ and /[[:digit:]]/ are not necessarily the same as /[0-9]/. See "perlre: /a (and /aa)".
More general sources of reference are: perlre; perlrebackslash; and, perlrecharclass.
— Ken
|
|---|