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
°) which involves redirecting STDERR ... Yuck!!!
Re: Distiguishing arguments: number-strings vs real integer
by choroba (Cardinal) 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,
| [reply] [d/l] |
|
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 ...
| [reply] |
|
| [reply] [d/l] |
|
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]
| [reply] |
|
|
|
|
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
| [reply] |
|
|
|
|
> 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.
| [reply] [d/l] [select] |
Re: Distiguishing arguments: number-strings vs real integer
by haukex (Archbishop) on Aug 10, 2018 at 07:10 UTC
|
See:
In your tests, you might want to add a case where both IOK and POK are set:
$ perl -MDevel::Peek -e 'my $x=0; $x eq 0 and Dump($x)'
SV = PVIV(0x1542e82) at 0x1548d10
REFCNT = 1
FLAGS = (IOK,POK,pIOK,pPOK)
IV = 0
PV = 0x1537840 "0"\0
CUR = 1
LEN = 10
| [reply] [d/l] [select] |
Re: Distiguishing arguments: number-strings vs real integer
by Tux (Canon) on Aug 10, 2018 at 12:55 UTC
|
$ 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
| [reply] [d/l] [select] |
Re: Distiguishing arguments: number-strings vs real integer
by tinita (Parson) on Aug 10, 2018 at 14:50 UTC
|
| [reply] |
|
| [reply] |
Re: Distiguishing arguments: number-strings vs real integer
by clueless newbie (Curate) on Aug 10, 2018 at 01:49 UTC
|
use autobox::universal qw(type);
say type("42"); # STRING
say type(42); # INTEGER
say type(42.0); # FLOAT
say type(undef); # UNDEF
| [reply] [d/l] |
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.
| [reply] |
|
| [reply] |
|
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,
| [reply] [d/l] |
|
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) | [reply] [d/l] |
Re: Distiguishing arguments: number-strings vs real integer
by wjw (Priest) on Aug 09, 2018 at 23:32 UTC
|
#!/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...
| [reply] [d/l] |
|
> Do I misunderstand the question ...
I'm afraid you are. :)
| [reply] |
|
|