Понимание кода: Hash, grep для дубликатов (изменен для проверки нескольких элементов) - PullRequest
6 голосов
/ 09 июля 2011

Код:

@all_matches = grep
{
    ! ( $seensentence
    {
        $_->[0] .'-'. $_->[1] .'-'. $_->[5]
    }
    ++ )
}
@all_matches;

Цель: Этот код удаляет дубликаты некоторых элементов из массива @all_matches, который является AoA.

Моя попытка полного срыва (где ?? .. ?? где я не уверен):

Grep возвращает элементы @all_matches, которые возвращают true.

Ключ хеша %seensentence - это три элемента @all_matches. Поскольку хеш может иметь только уникальные ключи, в первый раз значение увеличивается с undef (0) до 1. В следующий раз это определенное значение, но ! означает, что grep возвращает его, только если undef ( уникальное значение, связанное с этим элементом).


Мои вопросы:

(1) Как я могу превратить {$_->[0] .'-'. $_->[1] .'-'. $_->[5]}++ в HoH?

Мне сказали, что это еще один (идиоматический) способ сделать это. Удар в темноте будет:

( {$_->[0] => 0,
$_->[1] => 0,
$_->[5] => 0} )++

(1b) Потому что Я не понимаю , как оригинал делает то, что я хочу. Я прочитал, что -bareword эквивалентно "-bareword", поэтому я попытался: {"$_->[0]" . "$_->[1]". "$_->[5]"}, и, похоже, он работал точно так же. Тем не менее я не понимаю: рассматривает ли он каждый элемент как ключ (a) отдельно (как массив ключей) или (b) Правильно : все одновременно (так как . объединяет их все в одну строку) или это (с) не делает то, что я думаю?

(2) Что это значит: $_->[0] || $_->[1] || $_->[5]? Это не так, как описано выше.

Я читал, что: логические операторы короткого замыкания возвращают последнее значение, поэтому он будет проверять значение на {$_->[0]}, и если оно было, я думал, что значение будет увеличиваться, если нет, то будет проверять следующий элемент до ни один из них не был правдой, когда grep передает уникальное значение.


Спасибо за ваше время, я старался быть максимально тщательным (до ошибки?), Но сообщите мне, если чего-то не хватает.

Ответы [ 2 ]

5 голосов
/ 09 июля 2011

Сначала давайте превратим grep в цикл foreach, чтобы мы могли рассмотреть его более четко.Я собираюсь расширить некоторые идиомы в более крупные конструкции для ясности.

my @all_matches = ( ... );
{
    my %seen;
    my @no_dupes;
    foreach my $match ( @all_matches ) {
        my $first_item  = $match->[0];
        my $second_item = $match->[1];
        my $third_item  = $match->[5];
        my $key = join '-', $first_item, $second_item, $third_item;
        if( not $seen{ $key }++ ) {
            push @no_dupes, $match;
        }
    }
    @all_matches = @no_dupes;
}

Другими словами, оригинальный кодер создает ключ хеша, используя ссылку на массив, хранящуюся в $ match, для каждого изреферентные индексы $match->[0], 1 и 5.Поскольку хеш-ключи уникальны, любые дубликаты будут отброшены, проверяя, существует ли ключ, прежде чем нажать на @no_dupes.

Механизм grep{} просто более эффективен в коде (т. Е. Быстрее вводить,и нет одноразовых переменных) идиома для достижения того же.Если это работает, зачем рефакторинг?Что не делает, что вам нужно улучшить?

Чтобы сделать то же самое с HoH, вы можете сделать это:

my @all_matches = ( ... );
{
    my %seen;
    my @no_dupes;
    foreach my $match ( @all_matches ) {
        my $first_item  = $match->[0];
        my $second_item = $match->[1];
        my $third_item  = $match->[5];
        if( not $seen{ $first_item }->{ $second_item }->{ $third_item }++ ) {
            push @no_dupes, $match;
        }
    }
    @all_matches = @no_dupes;
}

Что может быть переведено обратно в grep следующим образом:

my @all_matches = ( ... );
{
    my %seen;
    @all_matches = grep { not $seen{$_->[0]}->{$_->[1]}{$_->[5]}++ } @all_matches;
}

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

Суважение к оператору ||, это другое животное.Я не могу придумать какой-либо полезный способ использовать его в этом контексте.Оператор логического короткого замыкания, скажем, «$a || $b || $c» проверяет логическую истинность $a.Если это правда, он возвращает свое значение.Если это ложно, он проверяет $b таким же образом.Если это ложно, он проверяет $c таким же образом.Но если $a истинно, $b никогда не проверяется.Если $b истинно, $c никогда не проверяется.

4 голосов
/ 09 июля 2011

Ключ $ seensentence - это простая строка.Это выражение $_->[0] .'-'. $_->[1] .'-'. $_->[5] создает строку.Вот эквивалентное выражение: join '-', $_->[0], $_->[1], $_->[5].Похоже, предполагается, что элементов 0, 1 и 5 достаточно для идентификации дубликатов в @ all_matches.

Редактировать
Пропустил ваш последний вопрос.

$_->[0] || $_->[1] || $_->[5] возвращает

  • $_->[0], если $_->[0] не ложно (0, пустая строка, не определено),
  • $_->[1], если $_->[1] не ложно,
  • $_->[5] в противном случае.

Операторы ярлыков останавливаются, как только имеет смысл остановиться.В случае || это происходит, как только результатом становится не ложное значение.В случае && это как только результат ложный.

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