Скажем, у меня есть массив, и я знаю, что я собираюсь сделать много: "Есть ли массив с X?" чеки. Эффективный способ сделать это - превратить этот массив в хэш, где ключи являются элементами массива, а затем вы можете просто сказать
if($hash{X}) { ... }
Есть ли простой способ сделать это преобразование массива в хэш? В идеале, он должен быть достаточно универсальным, чтобы принять анонимный массив и вернуть анонимный хеш.
%hash = map { $_ => 1 } @array;
Он не так короток, как "@hash {@array} =..." решения, но те, которые требуют, чтобы хеш и массив уже были определены где-то в другом месте, тогда как это может принимать анонимный массив и возвращать анонимный хэш.
Что это значит, это взять каждый элемент в массиве и связать его с "1". Когда этот список пар (ключ, 1, ключ, 1, ключ 1) присваивается хэшу, нечетные числа становятся хеш-ключами, а четные - становятся соответствующими значениями.
@hash{@array} = (1) x @array;
Это хэш-фрагмент, список значений из хэша, поэтому он получает список-y @спереди.
От документы:
Если вы смущены тем, почему используете вместо этого '@' на хеш-сегменте "%", подумайте об этом так. тип кронштейна (квадратный или кудрявый) определяет, будет ли это массив или хэш. С другой hand, ведущий символ ('$' или '@') на массиве или хеше указывает, вы возвращаетесь к исключительной ценности (скаляр) или множественное число (список).
@hash{@keys} = undef;
Синтаксис здесь, где вы ссылаетесь на хэш с @
, - это хэш-фрагмент. Мы в основном говорим $hash{$keys[0]}
И $hash{$keys[1]}
И $hash{$keys[2]}
... - это список в левой части =, lvalue, и мы назначаем этот список, который фактически переходит в хэш и устанавливает значения для всех названных клавиш. В этом случае я указывал только одно значение, поэтому значение переходит в $hash{$keys[0]}
, а остальные хэш-записи все автоматически оживляют (оживают) значениями undefined. [Мое первоначальное предложение здесь было установлено выражение = 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?"? check - преобразование массива в хэш. Эффективность зависит от скудного ресурса, часто от времени, а иногда от места, а иногда и от программных усилий. Вы, по крайней мере, удваиваете память, потребляемую, одновременно сохраняя список и хэш списка. Кроме того, вы пишете более оригинальный код, который вам нужен для тестирования, документа и т.д.
В качестве альтернативы рассмотрим модуль List:: MoreUtils, в частности функции any()
, none()
, true()
и false()
. Все они принимают блок как условный и список как аргумент, похожий на map()
и grep()
:
print "At least one value undefined" if any { !defined($_) } @list;
Я провел быстрый тест, загрузив половину /usr/share/dict/words в массив (25000 слов), а затем поискал одиннадцать слов, выбранных из всего словаря (каждое 5000-е слово) в массиве, используя как метод array-to-hash, так и функция any()
из списка:: MoreUtils.
На Perl 5.8.8, построенном из источника, метод array-to-hash работает почти на 1100x быстрее, чем метод any()
(1300x быстрее в Ubuntu 6.06, упакованном в Perl 5.8.7.)
Это не полная история - преобразование массива в хеш занимает около 0,04 секунды, что в этом случае снижает эффективность времени от массива до хэш-метода до 1,5x-2x быстрее, чем метод any()
. Все еще хорошо, но не так близко, как звездный.
У меня возникает ощущение, что метод массива-хэша в большинстве случаев будет бить any()
, но я бы чувствовал себя намного лучше, если бы у меня были более четкие показатели (много тестовых примеров, приличная статистическая анализы, возможно, алгоритмический анализ большого числа O для каждого метода и т.д.). В зависимости от ваших потребностей 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);
@keys-1
- это $#keys
.
Решение Ралди может быть затянуто до этого (нет необходимости в '= > ' от оригинала):
my %hash = map { $_,1 } @array;
Этот метод также можно использовать для превращения текстовых списков в хэши:
my %hash = map { $_,1 } split(",",$line)
Кроме того, если у вас есть такая строка значений: "foo = 1, bar = 2, baz = 3", вы можете сделать это:
my %hash = map { split("=",$_) } split(",",$line);
[EDIT включить]
Другое предлагаемое решение (которое принимает две строки):
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:: Junction.
use Perl6::Junction qw'any';
my @arr = ( 1, 2, 3 );
if( any(@arr) == 1 ){ ... }
Вы можете поместить код в подпрограмму, если вы не хотите загрязнять пространство имен.
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;
%hash = map{ $_, undef } @keylist
Если вы выполняете множество теоретико-множественных операций, вы также можете использовать Set:: Scalar или аналогичный модуль. Затем $s = Set::Scalar->new( @array )
построит для вас Set - и вы можете запросить его с помощью: $s->contains($m)
.
#!/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'
};
Вы также можете проверить Tie:: IxHash, который реализует упорядоченные ассоциативные массивы. Это позволит вам выполнять оба типа поиска (хэш и индекс) на одной копии ваших данных.