Как этот Perl grep работает для определения объединения нескольких хэшей?

Я не понимаю последнюю строку этой функции из Программирование Perl 3e.

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

@common = inter( %foo, %bar, %joe );
sub inter {
    my %seen;
    for my $href (@_) {
        while (my $k = each %$href) {
            $seen{$k}++;
        }
    }
    return grep { $seen{$_} == @_ } keys %seen;
}

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

3 ответов


grep будет принимать список, переданный ему (в этом случае каждый элемент, видимый в любом из hashrefs); и возвращает список только тех элементов, где выражение в блоке истинно (локально установка $_ переменной для каждого элемента в списке).

давайте посмотрим, как это выражение оценивается:

  • @_ - массив всех параметров, переданных подпрограмме - в нашем случае список переданных хэш-ссылок в.

  • на $seen{$_} == @_ выражение, что список принудительно в контекст скалярный (из-за ==).

  • при использовании в скалярном контексте список оценивает количество элементов в списке - в приведенном выше примере вызов до 3, так как было передано 3 hashrefs.

Итак, для каждого ключа в %seen (например, каждый ключ, видимый в любом из N hashrefs); выражение $seen{$_} == @_ численно сравнивает # раз элемент был замечен в хэшах до общего числа хэшей - он будет равен, конечно, только если элемент находится во всех хэшах, которые были переданы, и, следовательно, является членом пересечения, которое мы хотим.

Итак, чтобы подвести итог анализу, grep вернет список всех ключей которые происходят в каждом хэше (ака происходят N раз, где N - # хэшей). Е. Г. перекресток.


grep block list 

это будет применять блок к каждому элементу списка в свою очередь, элемент имеет псевдоним $_. Если блок возвращает true, элемент добавляется в возвращаемый массив.

в этом случае:

grep { $seen{$_} == @_ } keys %seen

блок $seen{$_} == @_, который сравнивает значение увиденного хэша против @_ . @_ вычисляется в скалярном контексте и таким образом возвращает количество элементов в @_ массив. @_ представляет аргументы текущей функции. В этом дело ( \%foo, \%bar, \%joe ), который возвращает 3 в скалярном контексте. Наш список keys %seen, который представляет собой массив, содержащий все ключи присутствуют в %seen.

эквивалентные английские высказывания:

  • "дайте мне список всех ключей от %seen где значение, связанное с этот ключ равен числу элементы, переданные этой функции"
  • "дайте мне список всех ключей от %seen где значение, связанное с этот ключ 3"
  • "дайте мне список все ключи от %seen что есть значение 3, то есть все ключи от %seen которые присутствуют в каждом из 3 hashrefs передается этой функции"

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

последняя строка greps список, возвращенный из keys %seen. Чтобы определить, появляется ли данный ключ во всех хэшах, переданных функции, мы можем сравнить значение этого ключа в %seen к числу аргументов в inter.

на grep блок, $_ устанавливается для каждого элемента keys список и протестирован для некоторых условий.

массив в скалярном контексте возвращает его длину. @_ - массив аргументов, переданных в подпрограмму. И == оператор помещает свои операнды в скалярный контекст, поэтому мы можем просто сравнить значение $seen{$_} на длине @_. Если они одинаковы, то этот ключ появился во всех хешей.