В Perl, как мне создать хэш, ключи которого берутся из заданного массива?

StackOverflow https://stackoverflow.com/questions/95820

  •  01-07-2019
  •  | 
  •  

Вопрос

Допустим, у меня есть массив, и я знаю, что мне придется выполнять множество проверок "Содержит ли массив X?".Эффективный способ сделать это - превратить этот массив в хэш, где ключами являются элементы массива, а затем вы можете просто сказать

if($hash{X}) { ... }

Есть ли простой способ выполнить это преобразование массива в хэш?В идеале он должен быть достаточно универсальным, чтобы принимать анонимный массив и возвращать анонимный хэш.

Это было полезно?

Решение

%hash = map { $_ => 1 } @array;

Это не такое короткое решение, как "@hash{@array} = ...", но для этих решений требуется, чтобы хэш и массив уже были определены где-то в другом месте, тогда как это может принимать анонимный массив и возвращать анонимный хэш.

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

Другие советы

 @hash{@array} = (1) x @array;

Это фрагмент хэша, список значений из хэша, поэтому перед ним отображается список-y @.

От документы:

Если вы не понимаете, почему вы используете символ "@" в срезе хэша вместо символа "%", подумайте об этом так. Тип скобки (квадратная или фигурная) определяет, является ли это просматриваемым массивом или хэшем.С другой силы, ведущие символ ('$' или '@') на массив или хэш-указывает, будет ли вы получаете обратно исключительное значение (скалярный) или множественный (список).

@hash{@keys} = undef;

Синтаксис здесь, где вы ссылаетесь на хэш с помощью @ это фрагмент хэша.По сути, мы говорим $hash{$keys[0]} И $hash{$keys[1]} И $hash{$keys[2]} ...это список в левой части =, значение lvalue, и мы присваиваем этому списку, который фактически переходит в хэш и устанавливает значения для всех именованных ключей.В этом случае я указал только одно значение, так что это значение переходит в $hash{$keys[0]}, а все остальные записи хэша автоматически оживляются (оживают) с неопределенными значениями.[Моим первоначальным предложением здесь было установить выражение = 1, которое установило бы для этого одного ключа значение 1, а для остальных - undef.Я изменил его для обеспечения согласованности, но, как мы увидим ниже, точные значения не имеют значения.]

Когда вы поймете, что значение lvalue, выражение в левой части =, представляет собой список, построенный на основе хэша, тогда начнет проясняться, почему мы это используем @.[За исключением того, что я думаю, что это изменится в Perl 6.]

Идея здесь в том, что вы используете хэш как набор.Важно не то значение, которое я присваиваю;это просто наличие ключей.Итак, то, что вы хотите сделать, это не что-то вроде:

if ($hash{$key} == 1) # then key is in the hash

вместо этого:

if (exists $hash{$key}) # then key is in the set

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

Обратите внимание, что при вводе if ( exists $hash{ key } ) если это не слишком большая работа для вас (которую я предпочитаю использовать, поскольку предметом интереса на самом деле является наличие ключа, а не правдивость его значения), то вы можете использовать короткий и приятный

@hash{@key} = ();

Здесь предполагается, что наиболее эффективный способ выполнить множество проверок "Содержит ли массив X?" - это преобразовать массив в хэш.Эффективность зависит от нехватки ресурсов, часто времени, но иногда и пространства, а иногда и усилий программиста.Вы, по крайней мере, удваиваете потребляемую память, сохраняя список и хэш списка одновременно.Кроме того, вы пишете больше оригинального кода, который вам нужно будет протестировать, задокументировать и т.д.

В качестве альтернативы, посмотрите на модуль List::MoreUtils, в частности на функции any(), none(), true() и false().Все они принимают блок в качестве условия и список в качестве аргумента, аналогично map() и grep():

print "At least one value undefined" if any { !defined($_) } @list;

Я провел быстрый тест, загрузив половину /usr/share/dict/words в массив (25000 слов), затем искал одиннадцать слов, выбранных из всего словаря (каждое 5000-е слово) в массиве, используя как метод преобразования массива в хэш, так и any() функция из списка::MoreUtils.

В Perl 5.8.8, построенном на основе исходного кода, метод преобразования массива в хэш выполняется почти в 1100 раз быстрее, чем any() метод (в 1300 раз быстрее под управлением Ubuntu 6.06 с пакетом Perl 5.8.7.)

Однако это не полная история - преобразование массива в хэш занимает около 0,04 секунды, что в данном случае снижает временную эффективность метода array-to-hash в 1,5-2 раза быстрее, чем any() способ.Все еще хорош, но далеко не так великолепен.

Мое внутреннее чутье подсказывает, что метод преобразования массива в хэш будет превзойден any() в большинстве случаев, но я чувствовал бы себя намного лучше, если бы у меня были более надежные показатели (множество тестовых примеров, приличный статистический анализ, возможно, какой-нибудь масштабный алгоритмический анализ каждого метода и т.д.) В зависимости от ваших потребностей, лучшим решением может быть List::MoreUtils;это, безусловно, более гибко и требует меньше кодирования.Помните, что преждевременная оптимизация - это грех...:)

Я всегда думал, что

foreach my $item (@array) { $hash{$item} = 1 }

было, по крайней мере, приятно и читабельно / ремонтопригодно.

В perl 5.10 есть оператор, близкий к волшебному ~~:

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

Смотрите здесь: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html

Также стоит отметить для полноты картины мой обычный метод для выполнения этого с помощью 2 массивов одинаковой длины @keys и @vals которые вы бы предпочли, чтобы были хэшем...

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);

Решение Ральди может быть ужесточено до этого ('=>' из оригинала не требуется).:

my %hash = map { $_,1 } @array;

Этот метод также может быть использован для превращения текстовых списков в хэши:

my %hash = map { $_,1 } split(",",$line)

Кроме того, если у вас есть строка значений, подобная этой:"foo = 1, bar = 2, baz = 3" вы можете сделать это:

my %hash = map { split("=",$_) } split(",",$line);

[ОТРЕДАКТИРОВАТЬ, чтобы включить]


Другим предлагаемым решением (которое занимает две строки) является:

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;

Вы также могли бы использовать Perl6:: Соединение.

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

Если вы выполняете много теоретико-множественных операций - вы также можете использовать Набор::Скалярный или аналогичный модуль.Тогда $s = Set::Scalar->new( @array ) создаст набор для вас - и вы сможете запросить его с помощью: $s->contains($m).

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

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

Или даже лучше:

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Если вы действительно хотите передать ссылку на массив:

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

Возможно, вы также захотите проверить Ничья::IxHash, который реализует упорядоченные ассоциативные массивы.Это позволило бы вам выполнять оба типа поиска (хэш и индекс) для одной копии ваших данных.

#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

выдает (обратите внимание, что повторяющиеся ключи получают значение в наибольшей позиции в массиве - т.е. 8-> 2, а не 6)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top