http://qs321.pair.com?node_id=111917

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

Just a quickie:
I have a function which does two different things depending on whether or not it is passed an integer or a string.

The problem is that sometimes the string I pass IS an integer! How can I tell the difference?

(On another note, whenever I preview this, I have to re-add the "1" in the title, as it disappears from the input box. Is this a bug?)

Replies are listed 'Best First'.
Re: 1 vs "1"
by davorg (Chancellor) on Sep 12, 2001 at 18:52 UTC

    You can't. Perl scalars can be used both as numbers and strings. Perl converts between the two internally and seamlessly.

    Actually, that's not completely true. You might be able to do some deep XS magic to find out whether the string slot or the number slot in the SV has been used, but I'm guessing that you don't really want to go there :)

    --
    <http://www.dave.org.uk>

    Perl Training in the UK <http://www.iterative-software.com>

Re: 1 vs "1"
by suaveant (Parson) on Sep 12, 2001 at 18:49 UTC
    There is no way to tell that I know of... in perl 1 is pretty much the same as "1"... however 01 is very different from "01" it's a tough one... do you know in advance if you are passing an int or a string... give a little more info...

                    - Ant
                    - Some of my best work - Fish Dinner

Re: 1 vs "1"
by dragonchild (Archbishop) on Sep 12, 2001 at 19:11 UTC
    It sounds like you're trying to do something in C, not Perl. If the string you're passing is an integer, then it's an integer, not a string!

    I'm not trying to be dense, but that's the way Perl looks at things. If you want to pass integers in, but treat them as strings, the easiest way I can think of it to pass a second variable in that would tell you if this scalar is to be viewed as an integer or string.

    Of course, I would personally write two functions, one for integers and one for strings. Then, you decide outside the function which to do. Right now, you've got a function doing two different things. Make each do one thing and one thing well.

    You can tell this really easily if your function has the form:

    sub foo { my ($a, $b, $c) = @_; if ($a == 1) { # Do something } else { # Do something else } }

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: 1 vs "1"
by ariels (Curate) on Sep 12, 2001 at 19:24 UTC
    You want to use the perlguts macros SvIOKp, SvNOKp, SvPOKp (test for internal representation as IV (integer), NV (double) or PV (string)). Here's Inline code to do it (you'll need to get the Inline module from CPAN):
    package types; use Inline C => <<'END_C'; int is_iv(SV *x) { return SvIOKp(x) != 0; } int is_nv(SV *x) { return SvNOKp(x) != 0; } int is_pv(SV *x) { return SvPOKp(x) != 0; } END_C 1;

    But I'm not sure I'd recommend doing this! Perl users expect to have all integers autostringified when needed. Also, I wouldn't try it on magic "double valued scalars" (which have different string and numeric values), like $!.

Re: 1 vs "1"
by nardo (Friar) on Sep 12, 2001 at 19:14 UTC
    On another note, whenever I preview this, I have to re-add the "1" in the title, as it disappears from the input box. Is this a bug?

    Yes. The resulting html is:
    <INPUT TYPE="text" name="node" value="1 vs "1"">
    The quote before the second 1 ends the value property. In other cases quotes get replaced with &quot; which displays it correctly in the textbox so it shouldn't be too difficult to fix this.
Re: 1 vs "1"
by Jeff Connelly (Acolyte) on Sep 13, 2001 at 03:18 UTC
    You may be able to exploit the ~ operator, it has deep knowledge of Perl's internals. For example:
    $string = "1"; $integer = 1; print ~$string; # Prints an extended character print ~$integer; # Prints 4294967294
    Much easier than messing with perlguts. But there's one caveat: using ~ on a string with extended characters results in ASCII characters; that is ~chr(205) is "2". Assuming your definition of "string" does not include extended characters (only ASCII), you should be able to get away with a function like this:
    sub is_integer { return (~$_[0] =~ m/^[0-9]+$/) ? 1 : 0; } print "1 ", is_integer(1), "\n"; # 1 ok print "'1' ", is_integer('1'), "\n"; # 0 ok print "chr(205) ", is_integer(chr(205)),"\n"; # 1 not ok
    That said, be careful with this power. You don't want to confuse your users. Problems occur when reading data from files or console, as everything read is a string unless explically changed via +0. This also doesn't work for floating point. But sometimes it may have useful uses.

    Hope this helps,

    Jeff Connelly
Re: 1 vs "1"
by George_Sherston (Vicar) on Sep 12, 2001 at 19:36 UTC
    I was staggered to learn how non-simple this seemingly simple problem is. Staggered, but also educated. This thread was a big help.

    § George Sherston
Re: 1 vs "1"
by Caillte (Friar) on Sep 12, 2001 at 20:50 UTC

    A limited method, but one that could be built on is to use +0. Using an eval like the example below will return 0 if fed a string containing no numeric characters or the number 0 so, something like this could be a start:

    foreach my $var ('0', '-112.04', 'qwerty') { $var2 = eval '$var+0'; if($var eq '0' or $var2) { print "Is an number\n"; } else { print "May not be an number\n"; } }

    $japh->{'Caillte'} = $me;

Re: 1 vs "1"
by MZSanford (Curate) on Sep 12, 2001 at 19:55 UTC
    not that i suggest this, but i bet Devel::Peek could be used to find out if the value passed is an IV/NV or PV ... but once again, not really suggested.
    my own worst enemy
    -- MZSanford
Re: 1 vs "1"
by riffraff (Pilgrim) on Sep 13, 2001 at 00:27 UTC
    You could also force the string to be looked at as a string, by, for example, prepending a character in front of it:

    $a=1; func($a); # a number func("x$a"); # now a string

    riffraff

(jeffa) Re: 1 vs "1"
by jeffa (Bishop) on Sep 12, 2001 at 19:02 UTC
    UPDATE: Oct, 11 2001
    Ok, now i know why davorg was getting on to me about my original post - first off, i misunderstood the question, secondly, my regex was quite pitiful.

    davorg was on to me because ...

    1. he knew that i own his book
    2. his book has a better regex

      Unless I misunderstood the original question, this doesn't answer it. I read it to mean that if you had two scalars like this:

      my $number = 1; my $string = "1";

      how would you tell which is a string and which is a number. I don't think your solution addresses this.

      Oh, and your regex to check for floating point numbers could use some work too :)

      --
      <http://www.dave.org.uk>

      Perl Training in the UK <http://www.iterative-software.com>