为什么 Types::Standard 打破 Scalar::Util::readonly?
Why does Types::Standard break Scalar::Util::readonly?
Perl 5.30.0。截至今天,图书馆是最新的。
如果散列是只读的,我希望 Scalar::Util::readonly 到 return 一些真实值,而且确实如此:
perl -MReadonly -M'Scalar::Util qw(readonly)' -E'
say readonly(%ENV);
Readonly::Hash %ENV => %ENV;
say readonly(%ENV);
'
0
134283264
...除非我也想使用Types::Standard,然后Scalar::Util::readonly不再有效?!
perl -MReadonly -M'Scalar::Util qw(readonly)' -MTypes::Standard -E'
say readonly(%ENV);
Readonly::Hash %ENV => %ENV;
say readonly(%ENV);
'
0
0
我查看了 Types::Standard 的未决问题,但没有直接描述我的问题。
这是怎么回事?
这不是使用 readonly
的正确方法。
不可能将散列传递给子程序。只有标量可以作为参数传递给 subs。原型可用于使它看起来像您正在将哈希传递给子项,但这里不是这种情况。
$ perl -E'
use Scalar::Util qw( readonly );
say prototype( "readonly" ) // "[none]";
'
$
那个原型的意思是
readonly( %ENV )
表示
&readonly( scalar( %ENV ) )
它不检查 %ENV
是否为 read-only;它检查在标量上下文中评估 %ENV
获得的值是否为 read-only。这是完全错误的。
Scalar::Util::readonly
不能用于检查散列(或数组)是否为 readonly
,只能用于标量。
那么如何检查哈希是否为 read-only?
嗯,Perl 提供了一个内置的子工程,就像 Scalar::Util::readonly
一样,叫做 Internals::SvREADONLY
。与 readonly
不同,SvREADONLY
适用于数组作为散列和标量。
$ perl -E'say prototype( "Internals::SvREADONLY" ) // "[none]";'
\[$%@];$
此原型导致传递对第一个参数的引用而不是参数本身。因此,
Internals::SvREADONLY( %x )
是
的缩写
&Internals::SvREADONLY( \%x )
事实是,由 Readonly::Hash
编辑的散列 return 实际上并不是 read-only。所以 Internals::SvREADONLY
和 Scalar::Util::readonly
一样没用。
$ perl -E'
use Readonly qw( );
say Internals::SvREADONLY( %x ) ?1:0;
Readonly::Hash %x => %x;
say Internals::SvREADONLY( %x ) ?1:0;
'
0
0
Readonly::Hash
使用 tie
拦截更改哈希的尝试。
$ perl -E'
use Devel::Peek qw( Dump );
use Readonly qw( );
Readonly::Hash %x => %x;
Dump( %x );
'
SV = PVHV(0x561f1e51b340) at 0x561f1e5435a8
REFCNT = 1
FLAGS = (RMG,OOK,SHAREKEYS) <--- No READONLY flag.
MAGIC = 0x561f1e558290
MG_VIRTUAL = &PL_vtbl_pack
MG_TYPE = PERL_MAGIC_tied(P) <--- tie() magic was added
MG_FLAGS = 0x02 to intercept attempts
REFCOUNTED to change the hash.
MG_OBJ = 0x561f1e515680
SV = IV(0x561f1e515670) at 0x561f1e515680
REFCNT = 1
FLAGS = (ROK)
RV = 0x561f1e5d39b8
SV = PVHV(0x561f1e51b400) at 0x561f1e5d39b8
REFCNT = 1
FLAGS = (OBJECT,SHAREKEYS)
STASH = 0x561f1e5d3c88 "Readonly::Hash"
ARRAY = 0x0
KEYS = 0
FILL = 0
MAX = 7
AUX_FLAGS = 0
ARRAY = 0x561f1e541950
KEYS = 0
FILL = 0
MAX = 7
RITER = -1
EITER = 0x0
RAND = 0x2685e09f
以下是模块如何检查它是否已经进行了散列 read-only:
tied( %x ) =~ 'Readonly::Hash'
那么为什么使用Readonly::Hash
后的输出差异呢?
虽然这个问题没有实际意义,但它仍然是一个有趣的问题。
嗯,这是由于 Readonly::Hash
中的一个错误:它 return 在标量上下文中是错误的值。
$ perl -E'
use Readonly qw( );
my %x = ( a=>4, b=>5, c=>6 );
say scalar( %x );
Readonly::Hash %x => %x;
say scalar( %x );
'
3
1
当在标量上下文中使用 %x
时,Perl returns 散列中的元素数。[1]
另一方面,Readonly::Hash
添加的魔法使其在散列不为空时 return 为真值,在散列为空时为假值。
这就是区别所在。
Perl returns 是临时的,又名凡人标量。它被创建为包含 returned 值,并且在调用者有机会复制它后将被释放。没必要花时间去做 read-only.[2]
Readonly::Hash,另一方面,return不只是任何真或假标量。它 return 是由每个 Perl 运算符 return 编辑的非常相同的真和假标量,return 是真或假。不是副本,而是完全相同的标量,&PL_sv_yes
和 &PL_sv_no
.[3] 这些标量是 read-only.[4 ]
那么为什么Types::Standard会有效果呢?
虽然这个问题没有实际意义,但它仍然是一个有趣的问题。
不幸的是,我还没有弄清楚。
情况并非总是如此。它从未 return 只是 true/false,但旧值实际上仅用作 true/false 值。
有点难度,你可以修改它(my $r = \scalar(%x); ++$$r
),但没有意义。这样做对散列没有影响。
琐事:与 &PL_sv_undef
一起,它们是仅有的三个 statically-allocated 标量。
它们是 read-only 因为我们不希望 4 == 5
开始 return 一个真实的值,因为 &PL_sv_no
被意外更改了。
Perl 5.30.0。截至今天,图书馆是最新的。
如果散列是只读的,我希望 Scalar::Util::readonly 到 return 一些真实值,而且确实如此:
perl -MReadonly -M'Scalar::Util qw(readonly)' -E'
say readonly(%ENV);
Readonly::Hash %ENV => %ENV;
say readonly(%ENV);
'
0
134283264
...除非我也想使用Types::Standard,然后Scalar::Util::readonly不再有效?!
perl -MReadonly -M'Scalar::Util qw(readonly)' -MTypes::Standard -E'
say readonly(%ENV);
Readonly::Hash %ENV => %ENV;
say readonly(%ENV);
'
0
0
我查看了 Types::Standard 的未决问题,但没有直接描述我的问题。
这是怎么回事?
这不是使用 readonly
的正确方法。
不可能将散列传递给子程序。只有标量可以作为参数传递给 subs。原型可用于使它看起来像您正在将哈希传递给子项,但这里不是这种情况。
$ perl -E'
use Scalar::Util qw( readonly );
say prototype( "readonly" ) // "[none]";
'
$
那个原型的意思是
readonly( %ENV )
表示
&readonly( scalar( %ENV ) )
它不检查 %ENV
是否为 read-only;它检查在标量上下文中评估 %ENV
获得的值是否为 read-only。这是完全错误的。
Scalar::Util::readonly
不能用于检查散列(或数组)是否为 readonly
,只能用于标量。
那么如何检查哈希是否为 read-only?
嗯,Perl 提供了一个内置的子工程,就像 Scalar::Util::readonly
一样,叫做 Internals::SvREADONLY
。与 readonly
不同,SvREADONLY
适用于数组作为散列和标量。
$ perl -E'say prototype( "Internals::SvREADONLY" ) // "[none]";'
\[$%@];$
此原型导致传递对第一个参数的引用而不是参数本身。因此,
Internals::SvREADONLY( %x )
是
的缩写&Internals::SvREADONLY( \%x )
事实是,由 Readonly::Hash
编辑的散列 return 实际上并不是 read-only。所以 Internals::SvREADONLY
和 Scalar::Util::readonly
一样没用。
$ perl -E'
use Readonly qw( );
say Internals::SvREADONLY( %x ) ?1:0;
Readonly::Hash %x => %x;
say Internals::SvREADONLY( %x ) ?1:0;
'
0
0
Readonly::Hash
使用 tie
拦截更改哈希的尝试。
$ perl -E'
use Devel::Peek qw( Dump );
use Readonly qw( );
Readonly::Hash %x => %x;
Dump( %x );
'
SV = PVHV(0x561f1e51b340) at 0x561f1e5435a8
REFCNT = 1
FLAGS = (RMG,OOK,SHAREKEYS) <--- No READONLY flag.
MAGIC = 0x561f1e558290
MG_VIRTUAL = &PL_vtbl_pack
MG_TYPE = PERL_MAGIC_tied(P) <--- tie() magic was added
MG_FLAGS = 0x02 to intercept attempts
REFCOUNTED to change the hash.
MG_OBJ = 0x561f1e515680
SV = IV(0x561f1e515670) at 0x561f1e515680
REFCNT = 1
FLAGS = (ROK)
RV = 0x561f1e5d39b8
SV = PVHV(0x561f1e51b400) at 0x561f1e5d39b8
REFCNT = 1
FLAGS = (OBJECT,SHAREKEYS)
STASH = 0x561f1e5d3c88 "Readonly::Hash"
ARRAY = 0x0
KEYS = 0
FILL = 0
MAX = 7
AUX_FLAGS = 0
ARRAY = 0x561f1e541950
KEYS = 0
FILL = 0
MAX = 7
RITER = -1
EITER = 0x0
RAND = 0x2685e09f
以下是模块如何检查它是否已经进行了散列 read-only:
tied( %x ) =~ 'Readonly::Hash'
那么为什么使用Readonly::Hash
后的输出差异呢?
虽然这个问题没有实际意义,但它仍然是一个有趣的问题。
嗯,这是由于 Readonly::Hash
中的一个错误:它 return 在标量上下文中是错误的值。
$ perl -E'
use Readonly qw( );
my %x = ( a=>4, b=>5, c=>6 );
say scalar( %x );
Readonly::Hash %x => %x;
say scalar( %x );
'
3
1
当在标量上下文中使用 %x
时,Perl returns 散列中的元素数。[1]
另一方面,Readonly::Hash
添加的魔法使其在散列不为空时 return 为真值,在散列为空时为假值。
这就是区别所在。
Perl returns 是临时的,又名凡人标量。它被创建为包含 returned 值,并且在调用者有机会复制它后将被释放。没必要花时间去做 read-only.[2]
Readonly::Hash,另一方面,return不只是任何真或假标量。它 return 是由每个 Perl 运算符 return 编辑的非常相同的真和假标量,return 是真或假。不是副本,而是完全相同的标量,&PL_sv_yes
和 &PL_sv_no
.[3] 这些标量是 read-only.[4 ]
那么为什么Types::Standard会有效果呢?
虽然这个问题没有实际意义,但它仍然是一个有趣的问题。
不幸的是,我还没有弄清楚。
情况并非总是如此。它从未 return 只是 true/false,但旧值实际上仅用作 true/false 值。
有点难度,你可以修改它(
my $r = \scalar(%x); ++$$r
),但没有意义。这样做对散列没有影响。琐事:与
&PL_sv_undef
一起,它们是仅有的三个 statically-allocated 标量。它们是 read-only 因为我们不希望
4 == 5
开始 return 一个真实的值,因为&PL_sv_no
被意外更改了。