Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Re: Is A Number

by kcott (Archbishop)
on Dec 17, 2021 at 20:02 UTC ( [id://11139691]=note: print w/replies, xml ) Need Help??


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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11139691]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-03-28 17:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found