Почему хеш-ключи имеют другой порядок при печати?

Я хочу создать несколько хэшей, используя одни и те же ключи, и чтобы ключи печатались одинаково. Итак, в приведенном ниже примере ключи $hash1 а также $hash2 должен всегда иметь один и тот же порядок, но при создании хэша не должно быть необходимости сохранять этот порядок.

use Data::Dumper;

my $hash1 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $hash2 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

print Dumper $hash1, $hash2;

Но вывод выглядит следующим образом:

$VAR1 = {
          'key1' => 1,
          'keyc' => 2,
          'keyb' => 4,
          'keya' => 3
        };
$VAR2 = {
          'keyb' => 4,
          'keya' => 3,
          'keyc' => 2,
          'key1' => 1
        };

то есть хэши имеют другой и неожиданный порядок. Что не так с моим Perl?

Моя версия Perl:

This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Обратите внимание: я знаю, что ключи хэша perl не отсортированы. Я хочу, чтобы у них был один и тот же порядок, но не нужно иметь отсортированный порядок. Я надеюсь, что смогу получить такой же вывод на печать, если снова запусту код.

Следуя советам из ответов, я установил две переменные среды:

PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0

Тогда я могу получить тот же вывод, когда я запускаю код повторно.

3 ответа

Решение

При печати хэша есть несколько различных понятий порядка: "порядок вставки", "порядок сортировки" и "случайный". Смотрите раздел ОКРУЖАЮЩАЯ СРЕДА perlrun документация для обсуждения способов, которыми вы можете управлять этим поведением, а также причин, по которым по умолчанию используется хэширование.

В течение по крайней мере десятилетия хэши в perl не гарантировали порядок ключей. В последнее время рандомизация хэшей была частью общей меры по усилению безопасности. Есть веские причины для рандомизации хэшей. Для более подробной информации смотрите perlsec обсуждение алгоритмической сложности атак. В документации по безопасности Perl вы заметите, что в perl-5.18 - если вы видите другое поведение по сравнению с предыдущими версиями, это может быть связано с этими последними изменениями.

Помимо явной сортировки ваших ключей хеша, существуют и другие подходы, которые вы можете использовать для упорядочивания ваших хэшей: Hash::Ordered это один пример. Hash::Ordered В документации есть хорошее обсуждение плюсов и минусов ряда других модулей.

В то время как хеш - это " неупорядоченная корзина " скаляров, расположенных в парах ключ-значение; массив - это " упорядоченная последовательность " скаляров [ 1 ]. " Срез " - это способ одновременного доступа к "нескольким элементам списка, массива или хэша". Срез использует @ sigil, так как операция возвращает список из нескольких значений - и с @ мы получаем "упорядоченную последовательность". В результате один из способов наложения своего рода "порядка" на хеш - это использование для доступа к нему фрагмента:

# We want alphabetical disorder ...
my %hashed = ( 1 => "z", 2 => "x", 3 => "y" );
for my $key ( keys %hashed ) { print $hashed{$key} } ;
__END__    
zyx

Мы хотим " zxy "не" zyx ". Чтобы наложить нашу произвольную версию порядка на этот хеш, мы сначала должны признать, что виновник здесь keys %hashed который возвращает ключи в случайном порядке. Решение состоит в том, чтобы sort ключи Ccurse и в этом надуманном примере мы храним их в @sort_order и использовать его, чтобы "вырезать" то, что мы хотим из хэша, так, как мы этого хотим:

my @sort_order = sort keys %hashed ;
print @hashed{@sort_order} ;
__END__
zxy

Тада!! Срезы могут быть полезны, когда вы хотите хранить ключи и значения в хэше, но обращаться к этим данным упорядоченным способом. Помните " @ "когда вы хотите нарезать хеш; как perldata ставит это: "вы используете '@'... на срез хеша... [потому что вы получаете... список ". И списки упорядочены.


[ 1 ] Определения хэшей как "неупорядоченных корзин" и массивов как "упорядоченной последовательности" взяты из превосходной статьи Майка Фридмана (FRIEDO) о массивах и списках в Perl.

Дальнейшие ссылки

Ничего плохого в вашем Perl, хэш не отсортирован.

Если вы хотите отсортировать по ключу, вам нужно сделать что-то вроде этого:

foreach my $key (sort keys %hash1) {
    print $key, $hash1{$key};
}

и то же самое для hash2...

Г. Чито ответ правильный. Если вы хотите отсортированный вывод из Data::Dumper, вы можете сделать:

use Data::Dumper;

my $hash1 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $hash2 = {
  keyc => 2,
  key1 => 1,
  keya => 3,
  keyb => 4,
};

my $dumper = Data::Dumper->new([$hash1, $hash2]);
$dumper->Sortkeys(1);
print $dumper->Dump;
Другие вопросы по тегам