【问题标题】:Different result when evaluating Readonly variable twice两次评估只读变量时的不同结果
【发布时间】:2014-01-22 20:15:44
【问题描述】:

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

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

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

我使用不带Readonly::XS 的 perl 5.10 和 Readonly 1.03。

我可以用

重现它
  • v5.10.0 on MSWin32-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

【问题讨论】:

  • 不适用于v5.16.3草莓(1000000000000001 x2)
  • 不在v5.14.4 linux 64 位,只读 1.04。
  • 无法在 Linux 上使用带有 bigint v0.23 的 perl v5.10.1、带有 bigint v0.34 的 perl v5.18.1 或带有 bigint v0.36 的 perl v5.19.8 的 Readonly v1.04 进行复制。
  • 也可以用 ActivePerl v5.12.0复现。

标签: perl operator-overloading readonly biginteger tie


【解决方案1】:

似乎是使用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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    相关资源
    最近更新 更多