Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Distiguishing arguments: number-strings vs real integer

by LanX (Archbishop)
on Aug 09, 2018 at 22:51 UTC ( #1220165=perlquestion: print w/replies, xml ) Need Help??

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

hi

I want to find out if a function is called with an integer or a string containing an integer.

Perl tries to hide what the scalar internally is, but Devel::Peek can be used here, the flag IOK shows an integer, POK a string.

looks_like_number() is no help here, I want the exact opposite.

Is there a more elegant way than catching and parsing the output of Dump?°

NB: Using PerlGuts optree vodoo is considered helpful but not more elegant ... ;)

use strict; use warnings; use Data::Dump qw/pp dd/; use Scalar::Util qw/looks_like_number/; use Devel::Peek; sub is_string { Dump($_[0]); warn "$_[0] looks_like_number\n\n\n" if looks_like_number($_[0]); } is_string(1); is_string("42"); my $n=1; my $s="42"; is_string($n); is_string($s); $n.=""; # cast to string $s+=0; # cast to num is_string($n); is_string($s);

SV = IV(0x228e98) at 0x228ea8 REFCNT = 1 FLAGS = (PADTMP,IOK,READONLY,pIOK) IV = 1 1 looks_like_number SV = PV(0x24d378) at 0x245af8 REFCNT = 1 FLAGS = (PADTMP,POK,READONLY,pPOK) PV = 0x22e078 "42"\0 CUR = 2 LEN = 16 42 looks_like_number SV = IV(0x28ae40) at 0x28ae50 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1 1 looks_like_number SV = PV(0x3bd068) at 0x28a6a0 REFCNT = 1 FLAGS = (PADMY,POK,pPOK) PV = 0x26a1078 "42"\0 CUR = 2 LEN = 16 42 looks_like_number SV = PVIV(0x234c40) at 0x28ae50 REFCNT = 1 FLAGS = (PADMY,POK,pPOK) IV = 1 PV = 0x26a1048 "1"\0 CUR = 1 LEN = 16 1 looks_like_number SV = PVIV(0x234c58) at 0x28a6a0 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 42 PV = 0x26a1078 "42"\0 CUR = 2 LEN = 16 42 looks_like_number

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

°) which involves redirecting STDERR ... Yuck!!!

Replies are listed 'Best First'.
Re: Distiguishing arguments: number-strings vs real integer
by choroba (Bishop) on Aug 09, 2018 at 23:39 UTC
    The real question is Why do you need it? Smells of an XY problem.

    E.g. JSON modules try to detect the fact and keep the nature of numbers and strings, and in my talk at Glasgow, I'll show why it's the wrong approach and what a more Perlish way could be. See also my blog.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      I'm hacking a DBI abstraction, and I'm not sure about the side-effects of binding 42 vs "42" to a placeholder.

      So I want to play safe and keep full control.

      see also should-i-quote-numbers-in-sql

      I hope that DBI handles that automatically according to the type of the column, but again I'm not sure ...

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        I'm hacking a DBI abstraction, and I'm not sure about the side-effects of binding 42 vs "42" to a placeholder.

        Isn't irrelevant what Perl thinks of a variable's type when DB already knows/was told what the type of that column should be at create time? If DB column was declared as INT then don't quote (and possibly warn user of a type mismatch if any), else quote.

        Having said that I read in Programming the Perl DBI:

        It's equally simple to specify multiple bind values within one statement, since bind_ param( ) takes the index, starting from 1, of the parameter to bind the given value to. For example:
        
            $sth = $dbh->prepare( "
                        SELECT name, location
                        FROM megaliths
                        WHERE name = ?
                        AND mapref = ?
                        AND type LIKE ?
                    " );
            $sth->bind_param( 1, "Avebury" );
            $sth->bind_param( 2, $mapreference );
            $sth->bind_param( 3, "%Stone Circle%" );
        
        You may have noticed that we haven't called the quote( ) method on the values. Bind values are passed to the database separately from the SQL statement,[50] so there's no need to "wrap up" the value in SQL quoting rules.
        
            [50] This is not strictly true, since some drivers emulate placeholders by doing a textual replacement of the placeholders with bind values before passing the SQL to the database. Such drivers use Perl's internal information to guess whether each value needs quoting or not. Refer to the driver documentation for more information. [emphasis mine]
        
        I'm hacking a DBI abstraction, and I'm not sure about the side-effects of binding 42 vs "42" to a placeholder

        Therefore, you need to determine whether the placeholder needs to be 42 or "42".

        So I want to play safe and keep full control

        Therefore, either:
        1) having determined that the placeholder needs to be an integer, you provide $x + 0;
        or
        2) having determined that the placeholder needs to be a string, you provide "$x".

        But I don't see that you would need to determine whether $x is an integer or a string.
        Of course, things get a little more complex if there's a need to check the validity of the integer value, but that doesn't appear to be an issue (going by your description of the problem).

        Cheers,
        Rob
      > E.g. JSON modules try to detect the fact and keep the nature of numbers and strings,

      which solves my issue, thanks! :)

      $str=encode_json( [$_[0]] ); does the trick, I only need to check if /^\[".*"\]$/ is matching.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Distiguishing arguments: number-strings vs real integer
by haukex (Chancellor) on Aug 10, 2018 at 07:10 UTC
Re: Distiguishing arguments: number-strings vs real integer
by Tux (Abbot) on Aug 10, 2018 at 12:55 UTC

    You can use Data::Peek's DDual:

    $ perl -MDP -wE'sub foo{DDumper[DDual($_[0])]};foo(42);foo("42");' [ undef, 42, undef, undef, 0 ] [ 42, undef, undef, undef, 0 ] $ perl -MDP -wE'sub foo{DDual($_[0]);1;};foo(42);foo("42");' IV(42) PV: SV_UNDEF IV: IV(42) NV: SV_UNDEF RV: SV_UNDEF PV("42"\0) PV: PV("42"\0) IV: SV_UNDEF NV: SV_UNDEF RV: SV_UNDEF
    my ($pv, $iv, $nv, $rv, $hm) = DDual ($var [, $getmagic])

    Enjoy, Have FUN! H.Merijn
Re: Distiguishing arguments: number-strings vs real integer
by tinita (Parson) on Aug 10, 2018 at 14:50 UTC
      Great!

      Thanks Tina! :)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Distiguishing arguments: number-strings vs real integer
by clueless newbie (Deacon) on Aug 10, 2018 at 01:49 UTC
Re: Distiguishing arguments: number-strings vs real integer
by tobyink (Canon) on Aug 10, 2018 at 15:07 UTC

    I want to find out if a function is called with an integer or a string containing an integer.

    The best solution is to stop wanting that.

    Generally speaking, a function which expects a number shouldn't care whether the number was supplied as a string or not, and a function which expects a string shouldn't care if you neglected to wrap a numeric-looking string in the proper quote marks.

    If your function can be given either a number or a string and is supposed to do different things with each, consider rewriting it into two different functions, so the caller can choose which behaviour they want.

      I somehow agree with you, but I didn't design SQL and have the general desire to warn my user about such glitches.

      And if this dogma was generally true, this would mean that Data::Dumper was mis-designed.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        SQL is typed, each column has a type, each function's return value has a type.

        Data::Dumper tries to do the impossible. I've just finished the first part of my slides for Glasgow, they show the problems with various JSON modules, how their results are different in corner cases, how their versions differ in what they return, and how the same version could return different results under different Perl versions. You've chosen a long, dark, and distressful path.

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Distiguishing arguments: number-strings vs real integer
by Eily (Monsignor) on Aug 10, 2018 at 12:29 UTC

    Another solution, which IMHO is a little more elegant than having to parse the output of another module: Scalar::Util's isdual() will return true for a scalar that has both a number and string representation. As documented, this is true for a number used in string context and the reverse for a string.

    So a scalar is a number if it becomes dual after stringification, and is a string if it becomes dual after numification (and is a trap if already dual to begin with ;-) ).

    perl -MScalar::Util=isdual -E "@v = (0, 1, 2, '3', '4', '5'); push @v, + @v; push @v, $v[0].$v[3]; push @v, $v[1]+$v[4]; isdual($_) and say f +or @v" 0 4
    The push @v, @v is to check that a variable's copy is left unaffected by the stringification/numification of the original (yes it's a very weak and useless check :P). After that, I stringify $v[0] and numify $v[4]. (The values 2, and 5 are unused so not dual, 1 and 3 are used as number and string respectively, which they already were)

Re: Distiguishing arguments: number-strings vs real integer
by wjw (Priest) on Aug 09, 2018 at 23:32 UTC

    Do I misunderstand the question or will the following not work?

    Edit: Ok, I misunderstand the nature of the problem as noted by LanX in Re^2: Distiguishing arguments: number-strings vs real integer... My apologies.

    #!/usr/bin/perl use strict; use warnings; #use Data::Dump qw/pp dd/; #use Scalar::Util qw/looks_like_number/; #use Devel::Peek; sub is_string { my $arg = $_[0]; warn "$arg looks like number \n$&\n\n\n" if $arg =~ /(\d+)/; } is_string(1); is_string("42"); my $n=1; my $s="42"; my $s1="this contains the number 143"; my $s2 = "Awierd123number"; is_string($n); is_string($s); is_string($s1); is_string($s2); $n.=""; # cast to string $s+=0; # cast to number is_string($n); is_string($s);

    ...the majority is always wrong, and always the last to know about it...

    A solution is nothing more than a clearly stated problem...

      > Do I misunderstand the question ...

      I'm afraid you are. :)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (5)
As of 2019-11-20 19:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Strict and warnings: which comes first?



    Results (100 votes). Check out past polls.

    Notices?