【问题标题】:How does Perl decide to treat a scalar as a string or a number?Perl 如何决定将标量视为字符串还是数字?
【发布时间】:2010-09-24 18:11:26
【问题描述】:

考虑以下代码及其输出:

代码

#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $HOURS_PER_DAY = 24.0 * 1.0;
my $BSA = 1.7 * 1.0;
my $MCG_PER_MG = 1000.0 * 1.0;
my $HOURS_DURATION    = 20.0 * $HOURS_PER_DAY;
my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;
print Dumper($HOURS_PER_DAY);
print Dumper( $BSA);
print Dumper( $MCG_PER_MG);
print Dumper( $HOURS_DURATION );

输出

$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;

如您所见,第二个变量被视为字符串,而第一个和第四个变量被视为数字。有人知道底层逻辑是什么吗?

编辑添加的算术计算并不能完全解决问题(请参阅 $BSA 变量)。


$ perl -v

This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)

Copyright 1987-2007, Larry Wall

【问题讨论】:

    标签: perl string numbers scalar


    【解决方案1】:

    Data::Dumper 的工作是对数据进行序列化,您无法根据其输出告诉 perl 在内部对数据做了什么。 Devel::Peek 模块可以转储存储在变量中的底层标志和值。 Devel::Peek POD 解释了标志的重要性。

    #!/usr/bin/perl
    
    use warnings;
    use strict;
    use Devel::Peek;
    
    my $HOURS_PER_DAY = 24.0 * 1.0;
    my $BSA = 1.7 * 1.0;
    my $MCG_PER_MG = 1000.0 * 1.0;
    my $HOURS_DURATION    = 20.0 * $HOURS_PER_DAY;
    my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;
    
    Dump($HOURS_PER_DAY);
    Dump($BSA);
    Dump($MCG_PER_MG);
    Dump($HOURS_DURATION);
    
    __END__
    SV = PVNV(0xd71ff0) at 0xd87f90
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
      IV = 24
      NV = 24
      PV = 0
    SV = PVNV(0xd71fc8) at 0xd87f60
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,NOK,pIOK,pNOK)
      IV = 1
      NV = 1.7
      PV = 0
    SV = PVNV(0xd72040) at 0xd87f40
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
      IV = 1000
      NV = 1000
      PV = 0
    SV = IV(0xd8b408) at 0xd87f30
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,pIOK)
      IV = 480
    # compare the above output to output without the assignment to $dummy:
    SV = IV(0x7b0eb8) at 0x7adf90
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,pIOK)
      IV = 24
    SV = NV(0x7c7c90) at 0x7adf60
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,NOK,pNOK)
      NV = 1.7
    SV = IV(0x7b13d8) at 0x7adf40
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,pIOK)
      IV = 1000
    SV = IV(0x7b1408) at 0x7adf30
      REFCNT = 1
      FLAGS = (PADBUSY,PADMY,IOK,pIOK)
      IV = 480
    

    【讨论】:

      【解决方案2】:

      Perl 将变量视为字符串或数字的整个概念是有缺陷的。 Perl 会在您需要时以正确的方式处理变量,例如,算术运算符 总是 将其参数视为数字(假设您没有滥用运算符重载等)。

      你不应该担心这个:Perl 知道它在做什么。

      【讨论】:

        【解决方案3】:

        Data::Dumper 在变量的字符串表示上使用一个简单的模式来确定它是否是一个数字。来自源代码:

        ...
        elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
          $out .= $val;
        }
        else {               # string
        ...
        

        这与解释您观察到的行为的带有小数点的数字不匹配。

        【讨论】:

        • Perl 不记得变量的最后一次使用。当使用其中任何一个时,它确实记得变量同时是有效的整数、浮点数或字符串。 但这不会影响变量的语义(除了两种情况,按位运算符和系统调用)。
        【解决方案4】:

        强制数字上下文的快速肮脏方法:

        打印自卸车($HOURS_DURATION + 0.0);

        如果您关心数据的显示方式,那么简洁的方法是:-

        printf "%5.2d",$HOURS_DURATION;

        【讨论】:

          猜你喜欢
          • 2016-05-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-07
          • 1970-01-01
          相关资源
          最近更新 更多