Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Exploiting Perls idea of what is a number

by rovf (Priest)
on Jul 08, 2008 at 12:41 UTC ( #696196=perlquestion: print w/replies, xml ) Need Help??

rovf has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks!

As we all know, there is no real "number" or "string" data type in Perl, and Perl usually does The Right Thing depending on the context:

$x=15; $x.=4; $x+=2; # ==> 156
I am now looking for an elegant way of exploiting this in my program, in the following way:

I would like to compare two values for equality so that, iff Perl would agree to see them as a number, numerical equality should be used (i.e. '0' and '000' should be regarded equal), but if at least one of them is not a number, string equality should be used.

The only solution I came up with is very ugly and goes roughly like this:

$temp1=eval { 0+$value1 }; $temp1=$value1 if $@; Perl says it is not a number $temp2=eval { 0+$value2 }; $temp2=$value2; # By virtue of eval, we can now use string equality # even if the values were numbers before. if ($temp1 eq $temp2) { ... }
Needless to say that this is extremely awful. Of course one might argue that what I want to achieve is a bit weird too, but still I would like to know whether there is an easy way to do it. Basically, I'm looking for an operation which would leave a string unchanged, but normalizes a number so that for example '001' would turn into '1' (this would be done by my addition of zero for instance).

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: Exploiting Perls idea of what is a number
by moritz (Cardinal) on Jul 08, 2008 at 12:47 UTC
    use Scalar::Util qw(looks_like_number); sub compare { my ($x, $y) = @_: if (looks_like_number($x) && looks_like_number($y)){ return $x == $y; } else { return $x eq $y; } }
      My first thoughts were in the direction of Scalar::Util::looks_like_number, too ... but that always leads to the consideration of what should be done with strings like '3567rubb13h'. Perl will numify that to 3567, yet Scalar::Util::looks_like_number will tell you that it *doesn't* look like a number. (It amuses me that perl is capable of bestowing a non-zero numerical value on something that doesn't even "look like a number".)

      Does rovf want such a string to be kept as a string ? or should it instead be numified to 3567 ?

      Cheers,
      Rob
        what should be done with strings like '3567rubb13h'

        At the moment, this type of strings won't be a problem, but if they appear (for example, with (the same) measurement unit appended, such as "450KB"), it would be fine to have them interpreted as number.

        -- 
        Ronald Fischer <ynnor@mm.st>
Re: Exploiting Perls idea of what is a number
by Corion (Patriarch) on Jul 08, 2008 at 12:49 UTC

    If you approach the equivalence from the other direction, it becomes easier:

    Two strings are equal iff

    1. ... they are equal as characters
    2. or ... they are equal as numbers

    When described that way, the expression becomes:

    $a eq $b || $a == $b

    This has the small drawback of equating undef and the empty string with 0 as well though.

    Update It also has the other small drawback of considering all strings to be equal, which is a bit suboptimal...

      That has the large drawback of declaring that "foo" is the same as "cheesecake" (while emitting two warnings). :)

      - tye        

        Maybe !($a cmp $b || $a <=> $b) would work... nope.

        -Paul

        That has the large drawback of declaring that "foo" is the same as "cheesecake" (while emitting two warnings). :)

        In my particular application, a foo is indeed the same than a cheesecake!

        -- 
        Ronald Fischer <ynnor@mm.st>
      $a eq $b || $a == $b

      I think I was too early with my response. Your suggestion, as elegant as it looks, would fail if $a and $b are unequal strings. In this case $a eq $b would result false, and $a == $b would be evaluated, yielding an error message because the arguments are not numeric...

      -- 
      Ronald Fischer <ynnor@mm.st>

      Thank's a lot for the suggestions!! I think for my original problem I find your solution most convenient, but actually there is another part of the program where I now can replace my cumbersome regexp to test for numerics, by moritz's idea of using Scalar::Util.

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: Exploiting Perls idea of what is a number
by linuxer (Curate) on Jul 08, 2008 at 14:47 UTC

    What about the good old perlfaq4?

    perldoc -q determine presents (regex) ways to determine if a scalar contains a number...

    I checked Scalar::Util::looks_like_number and it refers to perlfaq4... ;o)

Re: Exploiting Perls idea of what is a number
by zentara (Archbishop) on Jul 08, 2008 at 17:10 UTC
    You might be interested in convert hex string to hex number. My conclusion? Perl makes text processing easy, but converting them to numbers can be tricky. Luckily, you usually don't need to do it too often.

    I'm not really a human, but I play one on earth CandyGram for Mongo
Re: Exploiting Perls idea of what is a number
by graff (Chancellor) on Jul 09, 2008 at 03:10 UTC
    The following snippet is almost certainly not what you want, but I find it instructive...
    #!/usr/bin/perl use strict; my $numrgx = qr/^-?[\d.]+(?:e[+-]\d{2})?$/; my @try_these = ( "0.2000000000000001", "0.3000000000000001" ); for ( @try_these ) { my $x = my $y = $_; $y .= "1"; my $same = ( $x eq $y ); if ( ! $same and ( $x =~ /^-?\.?\d/ and $x =~ /$numrgx/ ) and ( $y =~ /^-?\.?\d/ and $y =~ /$numrgx/ )) { $same = ( $x == $y ); } my $cmp = ( $same ) ? "is the same as" : "differs from"; print "$x $cmp $y\n"; }
    For me (macosx 10.4 on intel core 2 duo, perl 5.8.6), the output is:
    0.2000000000000001 differs from 0.20000000000000011 0.3000000000000001 is the same as 0.30000000000000011
    Have fun...

    BTW, the reason for the funny looking condition -- checking matches for both  /^-?\.?\d/ and for the more elaborate regex -- was that it seemed like an easy way to keep $numrgx relatively clear and simple, while still making sure that the string contains at least one digit (which $numrgx by itself does not guarantee).

    (updated the initial match condition that checks for at least one digit, so that it would only accept "\d", "-\d", ".\d" or "-.\d" at the beginning of the string; of course, $numrgx is still faulty, since it allows multiple periods)

    Another update -- I knew the regex approach above was silly, and I was curious to have a version that would make it easier to test for more boundary cases (where floating point limitations blur the "same/different" distinction), so here's a version with a better regex -- still not perfect, I suspect, but more fun because of the @ARGV options:

    #!/usr/bin/perl use strict; my $numrgx = qr/^-?(?:\d+\.?\d*|\d*\.\d+)(?:e[+-]?\d{1,2})?$/; my @try_these = ( @ARGV == 2 ) ? ( $ARGV[0] ) : ( "0.2000000000000001", "0.3000000000000001" ); for ( @try_these ) { my $x = $_; my $y = ( @ARGV == 2 ) ? $ARGV[1] : $_ . "1"; my $same = ( $x eq $y ); if ( ! $same and ( $x =~ /$numrgx/ ) and ( $y =~ /$numrgx/ )) { $same = ( $x == $y ); } my $cmp = ( $same ) ? "is the same as" : "differs from"; print "$x $cmp $y\n"; }
    With that, you can put a pair like "0.123e4 1.230e3" on the command line, and find out that these are the same; and if you do a pair like "0.123e45 1.230e44", these are different. (final update/fix was to add "?" after the first period in $numrgx)
Re: Exploiting Perls idea of what is a number
by syphilis (Archbishop) on Jul 08, 2008 at 13:32 UTC
    $temp1=eval { 0+$value1 }; $temp1=$value1 if $@; Perl says it is not a number
    Ummm ... under what circumstances can that first line of code possibly set $@ ?

    Update: moritz has provided a very plausible answer to that question.

    Cheers,
    Rob
      Under the influence of use warnings FATAL => qw(numeric);
        Under the influence of use warnings FATAL => qw(numeric);

        Actually, just writing use warnings; is sufficient. Example:

        use strict; use warnings; my $value="abc"; my $t=eval { 0+$value }; print "$@\n";
        prints Argument "abc" isn't numeric in addition (+) at U:\develsv\ARTS\playground\eval_test1.pl line 4.

        -- 
        Ronald Fischer <ynnor@mm.st>

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2023-11-28 13:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?