использование карты для извлечения хэш-набора (ключ, значение), соответствующего заданному значению - PullRequest
6 голосов
/ 08 апреля 2011

Я искал способ сделать следующее действие, используя функцию perl map: учитывая хеш, я хочу извлечь пары (ключ, значение), в которых значение равно или соответствует указанному параметру.

В моем примере я хочу извлечь пары (ключ, значение), где value = failed, но это также может быть выражение (то есть строка, начинающаяся с A, или REGEX). Поэтому в результате я хочу получить хеш, а не только таблицу ключей, соответствующих значению.

my %strings = (
    bla => "success",
    ble => "failed",
    bli => "failed",
    foo => "success",
    blo => "failed",
    bar => "failed",
    blu => "success"
);

my %failed_s = ();

while (my ($k, $v) = each %strings) {
    if ( $v eq 'failed' ) {$failed_s{$k} = $v};
};

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

my %failed_s = 
    map { { $_ => $strings{$_} } 
    if ( $strings{$_}./failed/ ) } 
    keys %strings;
my %failed_s = 
    map { ( $strings{$_} eq 'failed') &&
          ($_, $strings{$_}) } 
    keys %strings;

print "Dumper \%failed_s: \n" . Dumper(\%failed_s);

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

Ответы [ 4 ]

8 голосов
/ 08 апреля 2011

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

Так что, когда большинство Perlers думают о KVP, они думают "хэш".Но подумайте о том, как Perl Arrays представляют собой 1) списки, 2) очереди и 3) стеки, и вы увидите, что реализация не совсем соответствует концепции.

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

(Конечно, у меня, похоже, нет моих методов "хеш-сплайсинга" ...)

В любом случае, используя List::Pairwise, вы просто

use List::Pairwise qw<grepp>;
my %failed_s = grepp { $b eq 'failed' } %strings;

Я чувствую себя рекламным парнем, когда говорю «Просто посмотрите, как это лаконично!»

6 голосов
/ 08 апреля 2011

В вашем первом примере с картой вы возвращаете ссылки на хеш-элементы одного элемента, а это не то, что вам нужно.

Во втором случае оператор && налагает скалярный контекст на свои аргументы, поэтому список ($_, $strings{$_}) возвращает только последний элемент в списке.

То, что вы ищете:

my %failed_s = map {
    $strings{$_} eq 'failed' ? ($_, $strings{$_}) : () 
} keys %strings;

Где оператор ? : возвращает список, если условие истинно, ипустой список, если он ложный.

3 голосов
/ 08 апреля 2011

Я думаю, что карта + комбинация grep чище.

my %failed_s =
   map { $_ => $strings{$_} }
   grep { $strings{$_} eq 'failed' }
   keys(%strings);

Но поскольку grep может быть реализован с помощью карты,

my %failed_s =
   map { $_ => $strings{$_} }
   map { $strings{$_} eq 'failed' ? $_ : () }
   keys(%strings);

это можно сделать, используя только карту.

my %failed_s =
   map { $strings{$_} eq 'failed' ? ($_ => $strings{$_}) : () }
   keys(%strings);

Их проблема в том, что вы возвращали ложь, когда намеревались ничего не возвращать.

3 голосов
/ 08 апреля 2011

map FUNC, LIST действует на каждый элемент списка.Ваша функция может вернуть пустой список для некоторых элементов (как предполагает ответ Эрика Строма).Другой подход - использовать другой фильтр, чтобы map работал только с интересующими вас элементами:

my %failed_s = map { 
    $_ => $strings{$_} 
} grep { $strings{$_} eq 'failed' } keys %strings;
...