Perl - Is this a bug with look_like_number, or am I a dumb person?

This code:

#!/usr/bin/perl -w use strict; use Scalar::Util qw(looks_like_number); sub what_the_fudge { my $string = "foo 123 bar"; if ($string =~ /foo (.+) bar/) { if (looks_like_number($1)) { print "$1 looks like a number\n"; } else { print "$1 doesnt look like a number\n"; } } } &what_the_fudge; &what_the_fudge; &what_the_fudge; 

Displays this:

 123 doesnt look like a number 123 looks like a number 123 looks like a number 

Why can't he recognize him as a number for the first time? equals sign It puzzles me.

Some information about my environment:

OS: OSX 10.6.8

perl -e 'use Scalar::Util; print "$Scalar::Util::VERSION\n"' perl -e 'use Scalar::Util; print "$Scalar::Util::VERSION\n"' -> 1.19

perl -v -> This is perl, v5.10.0, created for darwin-thread-multi-2level (with two patches registered, see Perl -V for more details)

+4
perl
source share
3 answers

This is a bug that has been fixed in Scalar :: Util 1.20. ("Process overloaded and bound values" in the Changes file.)

The XS version looks_like_number failed to cope with the magic arguments. Magic is something that allows you to call code when performing certain operations on a variable (for example, choosing a value).

Solutions:

  • Upgrade to Scalar :: Util 1.20 or later.
  • Remove the compiled component of your Scalar :: Util to force the use of a Pure Perl implementation that does not suffer from this problem.
  • Use looks_like_number("$1") , which creates a non-magic copy of $1 with the correct value.

1.19

 int looks_like_number(sv) SV *sv PROTOTYPE: $ CODE: #if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION <5) if (SvPOK(sv) || SvPOKp(sv)) { RETVAL = looks_like_number(sv); } else { RETVAL = SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK); } #else RETVAL = looks_like_number(sv); #endif OUTPUT: RETVAL 

1.21:

 int looks_like_number(sv) SV *sv PROTOTYPE: $ CODE: SV *tempsv; if (SvAMAGIC(sv) && (tempsv = AMG_CALLun(sv, numer))) { sv = tempsv; } else if (SvMAGICAL(sv)) { SvGETMAGIC(sv); } #if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION <5) if (SvPOK(sv) || SvPOKp(sv)) { RETVAL = looks_like_number(sv); } else { RETVAL = SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK); } #else RETVAL = looks_like_number(sv); #endif OUTPUT: RETVAL 

Try the following:

 what_the_fudge("foo 123 bar"); what_the_fudge("foo baz bar"); what_the_fudge("foo 123 bar"); sub what_the_fudge { my $string = shift; 

I haven't really tried, but you should get

 Version of Scalar::Util: 1.19 doesnt look like a number looks like a number doesnt look like a number 
+4
source share

I made several changes to your program:

 #! /usr/bin/env perl # use strict; use warnings; use Scalar::Util qw(looks_like_number); print "Scalar version: $Scalar::Util::VERSION\n"; what_the_fudge(); what_the_fudge(); what_the_fudge(); sub what_the_fudge { my $string = "foo 123 bar"; if ($string =~ /foo (.+) bar/) { if ( looks_like_number $1 ) { print qq("$1" looks like a number\n); } else { print qq("$1" doesn't look like a number\n); } } } 

My Mac runs 10.8.6, and I use Perlbrew to test 5.8.9, 5.10, 5.12, and 5.18. From the first two I get the following:

 Scalar version: 1.19 "123" doesnt look like a number "123" looks like a number "123" looks like a number 

With two other versions, I get the following:

 Scalar version: 1.22 "123" looks like a number "123" looks like a number "123" looks like a number 

Then I made another small change to your program. Instead of just using $1 I set my $test = $1 and then ran looks_like_number( $test ) ;

After that I got the following:

 Scalar version: 1.19 "123" looks like a number "123" looks like a number "123" looks like a number 

One of the things you should understand is that $1 , $_ and their ilk are not only global variables, but always in the main namespace. This means that if you call a subroutine with $1 , it is possible that the subroutine may end up changing the value.

You should always assign your own variables (and preferably my variables) instead of relying on Perl special variables. This means that you are not using $_ in the while and for loops, but not processing @_ directly in the routines. And when you use regex, you should assign your own variables $1 , $2 , etc., as soon as you can.

By the way, the most interesting difference between Scalar :: Util versions 1.22 and 1.19 is that version 1.22 will use the compiled C code, if it exists. If not, it loads Scalar :: Util :: PP, which is a version of Perl. However, when I used Scalar::Util::PP in Perl 5.12 and 5.18, I still get the correct output.

Curiosity and curiosity

I tried modifying Scalar::Util to find out what was going on. However, I see:

 require List::Util; # List::Util loads the XS 

And the version of Perl code is enclosed in a large Eval statement. Interestingly, the side effect only gets into the first, although the routine sets the line and starts the regular expression. The same code, but performs two different methods.

Adding quotes around $1 makes it behave correctly:

 if ( looks_like_number "$1" ) { 

Also just printing it does the job:

 print "The value is " . $1 . "\n"; if ( looks_like_number $1 ) { 

Or assign it to another variable, but not using this variable:

 my $test = $1; if ( looks_like_number $1 ) { works now... 

Let us use the debugger and see if we can trace the problem:

 $ perl -d test.pl Loading DB routines from perl5db.pl version 1.31 Editor support available. Enter h or `hh' for help, or `man perldebug' for more help. main::(test.pl:7): print "Version of Scalar::Util: $Scalar::Util::VERSION\n"; DB<1> c Version of Scalar::Util: 1.19 "123" looks like a number. "123" looks like a number. "123" looks like a number. Debugged program terminated. Use q to quit or R to restart, use o inhibit_exit to avoid stopping after program termination, hq, h R or ho to get additional info. 

It works? Try the same command without the -d :

 $ perl test.pl Version of Scalar::Util: 1.19 "123" doesn't look like a number. "123" looks like a number. "123" looks like a number. 

How can you trace this problem if you cannot see it in the debugger?

+4
source share

According to ikega,

You are probably using the version of Scalar :: Util Pure Perl, which probably uses the regex that clobbers $ 1. look_like_number ("$ 1") will fix it.

Thanks! This is fixed.

+2
source share

All Articles