Почему этот случайный селектор хеш-значения всегда возвращает одно и то же значение при первом вызове? - PullRequest
7 голосов
/ 18 декабря 2011

Пытаясь понять, как решение refp для случайного выбора значения хеша, я заметил кое-что странное.

При повторных вызовах следующего сценария 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 не предоставил обновление .Почему форма arrayref не страдает от той же проблемы, что обсуждалась выше?:

[@_=%hash]->[1|rand@_] for 1 .. 10;            # First value is random

Ответы [ 2 ]

7 голосов
/ 18 декабря 2011

Я подозреваю, что существует состояние гонки, при котором @_ не определено в первой итерации цикла.

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;    

Не отличается. Все еще плохая практика - использовать переменную в том же операторе, который вы ей присвоили. Разница, я полагаю, в том, что приоритет несколько изменился, так что назначение оценивается первым.

4 голосов
/ 19 декабря 2011

Никаких условий гонки или что-либо, что связано с тем, является ли @_ «определенным» или нет, просто вопрос порядка операций.

Индексы для среза списка оцениваются до того, как список будет разрезан.(Документация не гарантирует это так или иначе.) Таким образом, на первой итерации @_ пусто, и аргумент для rand равен 1 | 0 (= 0).Исторически rand (0) вела себя как rand (1), хотя теперь это задокументировано как подверженное изменениям.Таким образом, индекс на первой итерации равен> = 0 и <1, и неявный int индексации принимает его за 0. </p>

Выборка элемента массива ([@_=%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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...