7

我注意到使用Readonly模块声明的变量,多次评估一个变量会产生不同的结果。

>perl -Mbigint -MReadonly -wE "Readonly my $V => 1; foreach (1..2) { say 0 + '1000000000000001' * $V };
1000000000000000
1000000000000001

这是为什么?似乎第一次用字符串解释变量,第二次用数字上下文解释变量。我的猜测是,如果它是数字,Math::BigInteger模块将重载'*'运算符,产生准确的结果。这是Readonly模块中的错误吗,有什么办法可以避免吗?

我使用 perl 5.10 和 Readonly 1.03 没有Readonly::XS.

我可以用

  • v5.10.0MSWin32-x86-multi-thread(ActivePerl)
  • v5.10.0在 Linux 上x86_64-linux-thread-multi
  • v5.12.0在 Windows 上 (ActivePerl)

v5.14.2但是, (ActivePerl)不会发生这种情况。

我还用 Readonly 1.04 复制了它。我不太确定这是否相关,但Scalar::Util::looks_like_number行为类似:

>perl -MReadonly -MScalar::Util -Mbigint -wE "say $Readonly::VERSION; Readonly my $V => 1; foreach (1..2) { say Scalar::Util::looks_like_number $V; }"
1.04
0
1
4

1 回答 1

2

似乎是使用tied 变量时重载的错误,该变量已在最新版本的 perl 中修复。以下示例程序显示了差异:

use strict;
use warnings;
use 5.010;

sub TIESCALAR {
  bless {}, 'main';
}

sub FETCH {
  say 'FETCH';
  shift;
}

use overload
  '+' => sub { say 'add called'; },
  '0+' => sub { say 'tonum called'; };

tie my $a, 'main';
my $b = bless {}, 'main';

say "adding non-tied (call $_): ", $b+1 for (1..2);
say "adding tied     (call $_): ", $a+1 for (1..2);

使用 Perl 输出v5.10.0

add called
adding non-tied (call 1): 1
add called
adding non-tied (call 2): 1
FETCH
tonum called
adding tied     (call 1): 2
add called
adding tied     (call 2): 1

Perl在第一次评估绑定变量时尝试0+在重载运算符之前进行数字转换,从而产生标准的 perl 算术。+在 perl 版本 >= 5.14 中,输出如预期:

add called
adding non-tied (call 1): 1
add called
adding non-tied (call 2): 1
FETCH
add called
adding tied     (call 1): 1
FETCH
add called
adding tied     (call 2): 1

来自perldoc overload

BUGS
....
       Before Perl 5.14, the relation between overloading and tie()ing was
       broken.  Overloading was triggered or not based on the previous
       class of the tie()d variable.

       This happened because the presence of overloading was checked too
       early, before any tie()d access was attempted.  If the class of the
       value FETCH()ed from the tied variable does not change, a simple
       workaround for code that is to run on older Perl versions is to
       access the value (via "() = $foo" or some such) immediately after
       tie()ing, so that after this call the previous class coincides with
       the current one.
于 2014-01-20T12:31:48.177 回答