今天,当我在perl中遇到以下行为时,我感到很惊讶:
sub f { die if %{ $_[0] }; 42 }
my %h;
$h{x} ||= f(%h); # we die. $_[0] references a hash with an 'x' key during f's run-time
相反,在相同的设置下,以下语句的行为不同。
$h{x} = $h{x} || f(%h); # $h{x} is now 42
分配或与分配与逻辑或记录的组合之间的潜在差异是否存在?
如果这是由于自动生动化造成的,那么autovivification
模块中似乎无法检测到该特定结构中的自动生动化是一个错误还是缺少功能?
关键信息:
-
||
和||=
短路,因此它们必须先评估左手边,然后再评估右手边。(这允许f() || die()
。( -
=
在计算其左侧操作数之前先计算其右侧操作数。(这允许$x = f($x)
。( -
||=
的左侧仅评估一次。 -
除非必要(即在左值上下文中(,否则哈希元素在访问时不会被激活(创建(。
让我们看看$h{x} = $h{x} || f(%h);
。
根据以上内容,我们得出以下结论:
- 只有最左边的
$h{x}
需要使元素生动起来 - 最左边的
$h{x}
在f
返回之后进行评估
让我们看看$h{x} ||= f(%h);
。
根据以上内容,我们得出以下结论:
$h{x}
必须在调用f
之前进行求值- 评估
$h{x}
必须产生一个可修改的值,因此它必须使$h{x}
生动起来
左值上下文
- 赋值运算符的左侧(包括
||=
( - 递增/递减前/后的操作数
的操作数
- Foreach循环列表表达式(因为
$_ = uc($_) for @a;
( - 子程序参数(由于
sub ucip { $_[0] = uc($_[0]) }
(,但参见";一个例外";下面 - 其他人
一个异常
如果将$h{x}
传递给sub,Perl实际上传递了一个神奇的标量,它代理$h{x}
。如果可能的话,这样做是为了防止活体化。它不便宜,但它避免了很多令人讨厌的惊喜。
Perl可以使用相同的机制延迟$h{x} ||= f(%h);
中的生动化,但这是不值得的。
关键的一点是,仅仅检查密钥并不能使其自动激活
perl -Mstrict -wE'my %h; if ($h{k}) { say "yes" }; say "Exists" if exists $h{k}'
不打印任何内容;密钥CCD_ 24尚未被添加到散列中。(需要更多的自动激活功能,比如测试嵌套键——然后必须创建最后一个键之前的键。(
这解释了这两种情况的区别:对于||=
,当子f
运行†时,$h{x}
被分配给,因此必须首先创建密钥x
才能进行该分配,而在另一种情况下,$h{x}
只在子f
运行之前进行检查,因此不会发生自动激活。
我不知道||=
的实际实现,也没有在文档中找到任何具体的声明。
†||=
被列为分配运算符