sm@sh has asked for the wisdom of the Perl Monks concerning the following question:

I know how to cast between integer and string representation, but how can I test for this?

Data::Dumper seems to know...

perl -MData::Dumper -e 'print Dumper([ 1, "1" ])'; $VAR1 = [ 1, '1' ];

Replies are listed 'Best First'.
Re: How can I test for the representation of an integer?
by Eily (Monsignor) on Apr 25, 2016 at 10:04 UTC

    This answer on stackoverflow seems to do what you want (based on the fact that the bitwise & operator has a different output depending on the type of the scalar) with pure perl. I suppose there is a XS module somewhere that lets you know precisely what there is inside the SV.

    But as stated in the above post, the fact that a variable has a numerical value does not only depend on its declaration, but also on whether or not it was ever used in numerical context:

    perl -MData::Dumper -E '@a = (1, "1", 1, "1", "1o"); say "$a[2]"; say +$a[4]+$a[3]; say Dumper \@a' 1 2 $VAR1 = [ 1, '1', 1, 1, '1o' ];

    Edit: added "output" in my parenthesis, because why would I write all the words on the first try?

Re: How can I test for the representation of an integer?
by clueless newbie (Deacon) on Apr 25, 2016 at 11:22 UTC
      Thanks - that's probably the most accessible way I've seen.
      Feels like it should be in Scalar::Util.
        Feels like it should be in Scalar::Util

        I don't think I would trust Scalar::Util to not fiddle about with the wrapping.
        For example, Scalar::Util's looks_like_number() returns true for a Math::BigInt object, even though the looks_like_number() XS function would return false.

        Cheers,
        Rob
      Thank you, very useful! I ended up with this:
      if (eval {require autobox::universal;}) { autobox::universal->import('type'); } else { print 'Warning: Cannot locate autobox/universal.pm in @INC (you may + need to install autobox)' . "\n" . "In Strawberry Perl you can get autobox like this:\n" . " 1. from a Windows Administrator (elevated) command prompt +...\n" . " 2. cd \\strawberry\\perl\\bin\n" . " 3. cpan autobox::universal\n\n"; eval("sub type { return '?';}"); }
Re: How can I test for the representation of an integer?
by haukex (Bishop) on Apr 25, 2016 at 10:07 UTC

    Hi sm@sh,

    I don't know much about the internals, but from what I do know I'd guess it might be looking at the POK/IOK flags:

    $ perl -MDevel::Peek -e 'Dump(1); Dump("1")'; SV = IV(0x21490e8) at 0x21490f8 REFCNT = 1 FLAGS = (IOK,READONLY,PROTECT,pIOK) IV = 1 SV = PV(0x2129fa0) at 0x2149168 REFCNT = 1 FLAGS = (POK,IsCOW,READONLY,PROTECT,pPOK) PV = 0x218fef0 "1"\0 CUR = 1 LEN = 10 COW_REFCNT = 0 $ perl -MDevel::Peek -e '$a=42; $a="Hello"; Dump($a)'; SV = PVIV(0x83d2e0) at 0x8321f0 REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) IV = 42 PV = 0x834920 "Hello"\0 CUR = 5 LEN = 10 COW_REFCNT = 1

    However, the much bigger question on my mind is why you need to know this? Perl "casts" between strings and numbers transparently, except for the case where a string can't be converted to a number, which you can determine beforehand with Scalar::Util's looks_like_number. Are you writing code that needs to peek behind the curtains more than that?

    Regards,
    -- Hauke D

      Here's why - two regexps which return an array with the same result, but for different reasons

      ~$ perl -MData::Dumper -e 'print Dumper("1" =~ /1/)' $VAR1 = 1; ~$ perl -MData::Dumper -e 'print Dumper("1" =~ /(1)/)' $VAR1 = '1';

        One is a match and the other is a count - you can tell that from the different ways you have constructed the regex. By choosing a confusing value to test against makes this less obvious. Instead try with "2":

        $ perl -MData::Dumper -e 'print Dumper("2" =~ /2/);' $VAR1 = 1; $ perl -MData::Dumper -e 'print Dumper("2" =~ /(2)/);' $VAR1 = '2';

        Hi sm@sh,

        I'm not quite sure I understand yet, could you provide some more context? Are you trying to determine whether a regex had capture groups or not? If so, how about this?

        "1" =~ /1/; print @->1?"yes\n":"no\n"; "1" =~ /(1)/; print @->1?"yes\n":"no\n"; __END__ no yes

        This uses the special variable @-, aka @LAST_MATCH_START.

        Update 2018-09-01: Actually, @+ will give you the number of capture groups! See my reply below for the difference.

        Regards,
        -- Hauke D

Re: How can I test for the representation of an integer?
by syphilis (Bishop) on Apr 25, 2016 at 12:31 UTC
    If I want to know whether a value is an integer value (IV) or not (as often happens), I just use the the XS function SvIOK().
    If it returns true then it's an IV, otherwise it's something else.
    #!perl -l use strict; use warnings; use Math::BigInt; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; int is_IV(SV * x) { if(SvIOK(x)) return 1; return 0; } EOC my $x = '42'; print is_IV($x); # 0 $x += 0; print is_IV($x); # 1 print is_IV(~0); # 1 (also UV) print is_IV('?'); # 0; my $mbi = Math::BigInt->new(1); print is_IV($mbi); # 0; print is_IV(2 ** 20); # 0 (NV)
    Cheers,
    Rob
      Thanks - that answers my question perfectly.
Re: How can I test for the representation of an integer?
by Discipulus (Abbot) on Apr 25, 2016 at 12:57 UTC
    You cant, or well by design you are invited to treat scalar in a very permissive way.

    From Modern Perl book:

    The internal data structure which represents a scalar in Perl 5 has a numeric slot and a string slot.

    The dualvar() function within the core Scalar::Util module allows you to manipulate both values directly within a single scalar. Similarly, the module’s looks_like_number() function returns true if the scalar value provided is something Perl 5 would interpret as a number.

    Infact:
    perl -MData::Dumper -E "say for Dumper([ 1, '1' ]),eval qq(1+'1'), len +gth qq(1....).1;" $VAR1 = [ 1, '1' ]; 2 6
    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: How can I test for the representation of an integer?
by Anonymous Monk on Apr 25, 2016 at 12:54 UTC
    #!/usr/bin/perl use strict; use warnings; use Scalar::Util qw(looks_like_number); my @list = qw(1 "1" "1o"); foreach my $foo (@list) { if( looks_like_number( $foo ) ) { print "number\n"; }else{ print "string\n"; } }

    Just use looks_like_number from Scalar::Util

      Trouble with that is it treats both 1, and '1' (and 1.0 and '1.0') as looking like numbers (albeit with strangely different values of true)

      use Scalar::Util qw(looks_like_number);; [0] Perl> print looks_like_number( $_ ) for 1, '1', 1.0, '1.0', '1o';; 4352 1 8704 5 0

      But the OP wants to distinguish between 1 stored internally as an integer; and '1' stored internally as a string.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
      In the absence of evidence, opinion is indistinguishable from prejudice.