Когда лучше использовать массив вместо хеша в Perl?

Скажем, у вас есть массив @a = qw/ a b c d/;

и хеш %a = ('a' => 1, 'b' => 1, 'c' => 1, 'd' => 1);

Есть ли ситуация, когда создание версии массива лучше, чем создание хэша (кроме того, когда вам нужно перебирать все значения как что-то вроде

for (@a){
    ....

В этом случае вам придется использовать keys %a, если вы пошли с хэшем)? Поскольку проверка того, является ли конкретное значение в хеше, всегда более эффективна, чем в массиве, правильно?

Ответ 1

    • Массивы индексируются по номерам.
    • Хеши привязаны к строкам.
    • Все индексы с наибольшим индексом существуют в массиве.
    • Хеши редко индексируются. (например, "a" и "c" могут существовать без "b".)

Существует много новых свойств. В первую очередь,

    • Массивы могут использоваться для хранения упорядоченных списков.
    • Было бы уродливо неэффективно использовать хэши таким образом.
    • Невозможно удалить элемент из массива, если он не является самым высоким индексированным элементом.
    • Вы можете удалить из упорядоченного списка, реализованного с помощью массива, хотя он неэффективен для удаления элементов, отличных от первого или последнего.
    • Можно удалить элемент из хеша, и он эффективен.

Ответ 2

Массивы - это упорядоченные списки значений. Они могут содержать повторяющиеся значения.

@array = qw(a b c a);

Хеши - это сопоставление между ключом (который должен быть уникальным) и значением (которое можно дублировать). Хэши (эффективно) неупорядочены, а это означает, что ключи выходят в явно случайном порядке, а не в порядке, в котором они введены.

%hash = (a => 1, b => 2, c => 3);

Хеши также могут использоваться как наборы, когда имеет значение только ключевой элемент. Наборы неупорядочены и содержат только уникальные "значения" (хеш-ключи).

%set = (a => undef, b => undef, c => undef);

Какой из них использовать зависит от ваших данных и алгоритма. Используйте массив, когда порядок имеет значение (особенно если вы не можете сортировать, чтобы получить порядок) или если возможны повторяющиеся значения. Используйте набор (т.е. Используйте хэш как набор), когда значения должны быть уникальными и не заботятся о порядке. Используйте хеш, когда имеет значение уникальность, порядок не сортируется (или легко сортируется), а поисковые запросы основаны на произвольных значениях, а не на целых числах.

Вы можете комбинировать массивы и хеши (через ссылки) для создания произвольно сложных структур данных.

@aoa = ([1, 2, 3], [4, 5, 6]);               # array of arrays ("2D" array)
%hoh = (a => { x => 1 }, b => { x => 2 });   # hash of hashes
@aoh = ({a => 1, b => 2}, {a => 3, b => 4}); # array of hashes
%hoa = (a => [1, 2], b => [3, 4]);           # hash of arrays
...etc.

Ответ 3

Об использовании чисел в качестве хеш-ключей. Он не отвечает на вопрос напрямую, так как он не сравнивает возможности, которые предоставляют массивы, но я думал, что это будет хорошее место для размещения информации.

Предположим, что хэш с десятью элементами построен с использованием кода типа

use strict;
use warnings;

my %hash;
my $n = 1000;
for (1 .. 10) {
  $hash{$n} = 1;
  $n *= 1000;
}

а затем мы запрашиваем его, ища ключи с десятью. Конечно, самый простой способ умножить целое число на десять - это добавить нуль, так что писать писать

my $m = '1';

for (1 .. 100) {
  print $m, "\n" if $hash{$m};
  $m .= 0;
}

который имеет выход

1000
1000000
1000000000
1000000000000
1000000000000000
1000000000000000000

Мы ввели десять элементов, но это показывает только шесть. Что произошло? Давайте посмотрим, что в хеше.

use Data::Dump;
dd \%hash;

и эти выходы

{
  "1000"                => 1,
  "1000000"             => 1,
  "1000000000"          => 1,
  "1000000000000"       => 1,
  "1000000000000000"    => 1,
  "1000000000000000000" => 1,
  "1e+021"              => 1,
  "1e+024"              => 1,
  "1e+027"              => 1,
  "1e+030"              => 1,
}

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

Для немного более практичного примера, скажем, у нас были некоторые круги и мы хотели собирать в по площади. Очевидно, что использовать эту область как хэш-ключ, как эта программа, которая создает 100 000 кругов со случайными целыми диаметрами до 18 миллионов.

use strict;
use warnings;
use 5.010;

package Circle;

use Math::Trig 'pi';

sub new {
  my $class = shift;
  my $self = { radius => shift };
  bless $self, $class;
}

sub area {
  my $self = shift;
  my $radius = $self->{radius};
  pi * $radius * $radius;
}



package main;

my %circles;

for (1 .. 100_000) {
   my $circle = Circle->new(int rand 18_000_000);
   push @{ $circles{$circle->area} }, $circle;
}

Теперь посмотрим, сколько из этих хэш-ключей использует научную нотацию

say scalar grep /e/, keys %circles;

который говорит (случайно, конечно)

861

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

Ответ 4

В Perl an @array - упорядоченный список значений ($v1, $v2, ...), к которому обращается целое число (как положительное, так и отрицательное) а хэш - это неупорядоченный список пар "key = > value" (k1 => $v1, k2 => $v2, ...), к которому обращается строка.

В CPAN есть модули, которые реализуют упорядоченные хэши, например: Hash::Ordered и Tie::IxHash

Возможно, вы захотите использовать массив, когда вы заказали "предметы", предположительно, большое количество, для которые используют хэш х% и сортировку ключей и/или значения будут неэффективными.