Perl - 这是look_like_number的错误,还是我是一个愚蠢的人?

时间:2013-08-12 23:57:12

标签: perl

此代码:

#!/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;

显示:

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

为什么它第一次无法将其识别为数字? =( 这令我感到困惑。

有关我的环境的一些信息:

操作系统:OSX 10.6.8

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

perl -v - >这是为darwin-thread-multi-2level构建的perl,v5.10.0(有2个已注册的补丁,有关详细信息,请参阅perl -V)

3 个答案:

答案 0 :(得分:4)

我在你的程序中做了一些修改:

#! /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);
        }
    }
}

我的Mac运行10.8.6,我使用Perlbrew测试5.8.9,5.10,5.12和5.18。前两个我得到了这个:

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

对于其他两个版本,我明白了:

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

然后我对你的程序做了另一个小改动。我不是简单地使用$1,而是设置my $test = $1,然后运行looks_like_number( $test );

完成后,我得到了这个:

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

您必须了解的一点是,$1$_及其同类不仅仅是全局变量,而且始终位于main命名空间中。这意味着如果使用$1调用子例程,则子例程可能最终会重置该值。

您应该始终分配自己的变量(最好是my变量),而不是依赖于特殊的Perl变量。这意味着不在$_while循环中使用for,也不在子例程中直接操作@_。而且,当您使用正则表达式捕获时,您应该尽快将自己的变量分配给$1$2等。

顺便说一句,Scalar :: Util版本1.22和1.19之间最有趣的区别是版本1.22将使用C编译代码(如果存在)。如果没有,它加载Scalar :: Util :: PP,它是Perl版本。但是,当我在Perl 5.12和5.18中使用Scalar::Util::PP时,我仍然得到正确的输出。

Curiouser和Curiouser

我尝试修改Scalar::Util以查看发生了什么。但是,我明白了:

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

Perl版本的代码包含在一个大的Eval语句中。有趣的是,副作用仅在第一个中被捕获 - 尽管子例程正在设置字符串,并运行正则表达式。相同的代码,但执行两种不同的方式。

$1附近添加引号使其行为正确:

if ( looks_like_number "$1" ) {

只需将其打印出来就可以了:

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

或者将其分配给另一个变量,但不使用该变量:

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

让我们使用调试器,看看我们是否可以追踪问题:

$ perl -d test.pl

Loading DB routines from perl5db.pl version 1.31
Editor support available.

Enter h or `h h' 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,
  h q, h R or h o to get additional info.

有效吗?让我们在没有-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.

如果您无法在调试器中看到它,如何追溯此问题?

答案 1 :(得分:4)

这是在Scalar :: Util 1.20中修复的错误。 (“处理Changes文件中的重载和绑定值。”

looks_like_number的XS版本未能正确处理神奇的论点。 Magic是允许在对变量执行某些操作时调用代码(例如获取其值)。

解决方案:

  • 升级到Scalar :: Util 1.20或更高版本。
  • 删除Scalar :: Util的已编译组件,以强制使用不受此问题影响的Pure Perl实现。
  • 使用looks_like_number("$1")创建具有正确价值的$1的非魔法副本。

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

试试这个:

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;

我还没有尝试过,但你应该

Version of Scalar::Util: 1.19
doesnt look like a number
looks like a number
doesnt look like a number

答案 2 :(得分:2)

据池上说,

  

您可能正在使用纯Perl版本的Scalar :: Util   可能使用正则表达式,其中包含1美元。 looks_like_number(“$ 1”)会   解决它。

谢谢!这解决了它。