在尝试了解refp's solution随机哈希值选择的工作原理时,我注意到了一些奇怪的事情。
重复调用以下Perl脚本后,我始终发现返回的第一个结果是相同的。返回的后续值是随机的:
use strict;
use warnings;
use 5.010;
my %hash = map { $_ => ord $_ } 'a' .. 'z';
say( (@_=%hash)[1|rand@_] ) for 1 .. 10; # First value always 119
有趣的是,以下内容不会受到此问题的影响:
sub random_value { ( @_ )[ 1 | rand @_ ] }
say random_value %hash for 1 .. 10; # First value is random
删除对@_
的引用也可以解决问题:
say( (%hash)[1|rand keys %hash] ) for 1 .. 10; # First value is random
已在Windows(ActivePerl 5.14.2)上测试过。
从表面上看,似乎设置@_
与它有关,但我不确定。任何人都可以了解这里发生的事情吗?
我认为这个问题在refp provided an update之前得到了回答。为什么arrayref形式没有遭受上面讨论的相同问题? :
[@_=%hash]->[1|rand@_] for 1 .. 10; # First value is random
答案 0 :(得分:7)
我怀疑存在竞争条件,其中@_
未在第一次循环迭代中定义。
say( (@_=%hash)[1|rand@_] ) for 1 .. 10;
将成为
say( (@_=%hash)[1|rand ()] ) for 1 .. 10;
它逃脱warnings
因为@_
是预先声明的变量。你会注意到:
say( (my @a=%hash)[1|rand@a] ) for 1 .. 10;
崩溃和刻录,因为后缀中未定义@a
。
更新
[@_=%hash]->[1|rand@_] for 1 .. 10;
没有任何不同。在您指定的同一语句中使用变量仍然是不好的做法。我猜,差异在于优先级有所改变,因此首先评估赋值。
答案 1 :(得分:4)
没有竞争条件,或与@_是否“已定义”无关,只是一个操作顺序。
在切片列表之前评估列表切片的索引。 (文档不保证这种方式或其他方式。)因此在第一次迭代时,@ _为空,rand的参数为1 | 0(= 0)。从历史上看,rand(0)表现得像rand(1),尽管现在记录为可以改变。因此,第一次迭代的索引是> = 0并且< 1,并通过索引的隐式int取0。
数组元素提取([@_=%hash]->[1|rand@_]
)不会遇到类似的问题,因为它会在数组操作数之后计算索引。另一方面,数组切片(@{[@_=%hash]}[1|rand@_]
)的行为与列表切片相同。
比较
列表切片:
$ perl -MO=Concise,-exec -e'(@_=%hash)[1|rand@_]'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <0> pushmark s
4 <$> const[IV 1] s
5 <#> gv[*_] s
6 <1> rv2av[t7] sK/1
7 <1> rand[t8] sK/1
8 <2> bit_or[t9] sK
9 <0> pushmark s
a <0> pushmark s
b <#> gv[*hash] s
c <1> rv2hv[t4] lK/1
d <0> pushmark s
e <#> gv[*_] s
f <1> rv2av[t2] lKRM*/1
g <2> aassign[t5] lKS/COMMON
h <2> lslice vK/2
i <@> leave[1 ref] vKP/REFC
-e syntax OK
数组切片:
$ perl -MO=Concise,-exec -e'@{[@_=%hash]}[1|rand@_]'
1 <0> enter
2 <;> nextstate(main 2 -e:1) v:{
3 <0> pushmark s
4 <$> const[IV 1] s
5 <#> gv[*_] s
6 <1> rv2av[t8] sK/1
7 <1> rand[t9] sK/1
8 <2> bit_or[t10] sK
9 <0> pushmark s
a <0> pushmark s
b <#> gv[*hash] s
c <1> rv2hv[t4] lK/1
d <0> pushmark s
e <#> gv[*_] s
f <1> rv2av[t2] lKRM*/1
g <2> aassign[t5] lKS/COMMON
h <@> anonlist sK*/1
i <1> rv2av[t6] sKR/1
j <@> aslice vK
k <@> leave[1 ref] vKP/REFC
-e syntax OK
数组元素:
$ perl -MO=Concise,-exec -e'[@_=%hash]->[1|rand@_]'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <0> pushmark s
4 <0> pushmark s
5 <#> gv[*hash] s
6 <1> rv2hv[t4] lK/1
7 <0> pushmark s
8 <#> gv[*_] s
9 <1> rv2av[t2] lKRM*/1
a <2> aassign[t5] lKS/COMMON
b <@> anonlist sK*/1
c <1> rv2av[t10] sKR/1
d <$> const[IV 1] s
e <#> gv[*_] s
f <1> rv2av[t7] sK/1
g <1> rand[t8] sK/1
h <2> bit_or[t9] sK
i <2> aelem vK/2
j <@> leave[1 ref] vKP/REFC
-e syntax OK