4

这段代码:

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

4

3 回答 3

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);
        }
    }
}

我有一台运行 10.8.6 的 Mac,我使用 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 变量。这意味着不使用$_inwhilefor循环,也不@_直接在子例程中进行操作。而且,当您使用正则表达式捕获时,您应该尽快将自己的变量分配给$1$2等。

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

越来越好奇

我尝试修改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.

如果在调试器中看不到这个问题,你怎么能找到它?

于 2013-08-14T15:27:29.630 回答
4

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

XS 版本looks_like_number无法正确处理魔法参数。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
于 2013-08-14T20:00:08.030 回答
2

据池上介绍,

您可能正在使用纯 Perl 版本的 Scalar::Util,它可能使用了正则表达式,这会花费 1 美元。looks_like_number("$1") 将修复它。

谢谢!这解决了它。

于 2013-08-13T11:58:02.957 回答