Perl: может кто-нибудь объяснить этот код? Это включает в себя карту, сортировку, tr и ссылки. (Модифицированное преобразование Шварца) - PullRequest
3 голосов
/ 29 мая 2011

Я читал учебные пособия и perldoc по map, tr и ссылкам, но этот код слишком сложен для начинающего пользователя Perl, такого как я.

print map $_->[1], 
sort {
$a->[0] cmp $b->[0] ##first element of the array
or $a->[1] cmp $b->[1] } 
map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches; 

Что мне особенно нужно, так это то, на что ссылается $ _ (undefined?)

Что делает последняя строка, включая карту?

Я пока не очень понимаю концепции $ a и $ b. На что они ссылаются? Первый и следующий элемент @allmatches?

Кроме того, что делают все запятые (после карты)? И если это похоже на преобразование Шварца, хорошо, потому что я этого еще не понимаю, несмотря на чтение.

Вот моя идея:
Отображает неопределенный скаляр как ссылку на массив (который?), Одновременно вызывая второй элемент: [1]. Он сортирует мой массив @allmatches сначала по количеству вхождений "MATCH", а затем по алфавиту. Вторая карта с указанием ссылок для меня довольно трудна (карты делают многое за один шаг); tr возвращает количество раз. Второй «МАТЧ» бесполезен, но почему?

Бонус: чем можно заменить tr /// для сортировки по большему, например, если бы это было возможно: tr / MATCH # \ d + // ??

Ответы [ 3 ]

14 голосов
/ 29 мая 2011

Чтение справа налево (т. Е. В порядке выполнения) ...

map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches;

Для каждого элемента e @allmatches создается ссылка на массив из двух элементовпервый элемент которого является числом, а второй элемент - e.Результатом карты является массив этих ссылок.

tr/"MATCH"/"MATCH"/ - это число раз, когда буквы e, A, T, C или H встречаются в e.(Технически он заменяет M на M, A на A, T на T и т. Д., И подсчитывает, сколько таких замен он сделал.)

На самом деле, он также считает символы кавычек, поскольку tr ///собирается обрабатывать их так же, как и все остальное.Это похоже на ошибку.

В любом случае, допустим, что каждая из этих ссылок относится к массиву [n, e], где n - странное число, а e - исходный элемент @ allmatches.

Затем «сортировка» сортирует массив ссылок, главным образом, по n (интерпретируется как строка, а не число; это, похоже, еще одна ошибка) и во вторую очередь по строке e.

Наконец,Самая внешняя «карта» извлекает второй элемент (e) из каждого из двухэлементных массивов после того, как сортировка выполнена.Таким образом, конечный результат - просто сделать причудливую (и я полагаю, ошибочную) сортировку элементов @ allmatches.

[Редактировать: Как указывает cjm в комментарии, эта map sort map идиома называется преобразование Шварца .]

10 голосов
/ 29 мая 2011

Не читайте справа налево;отформатируйте его лучше (оригинал был зверским), а затем прочитайте снизу вверх:

print map  { $_->[1] }
      sort {
              $b->[0] <=> $a->[0]
                      ||
              $a->[1] cmp $b->[1]
           }
      map  { [ tr/MATCH// => $_ ] }
      @allmatches;

Или используйте вместо этого более гибкие хеши:

print map  { $_->{DATA} }
      sort {
              $b->{COUNT} <=> $a->{COUNT}
                          ||
              $a->{DATA}  cmp $b->{DATA}
           }
      map  {
             +{
                COUNT  => tr/MATCH//,
                DATA   => $_,
              }
      } @allmatches;

Что, конечно, так же, как это:

print map  {         $$_{DATA}      }
      sort {
              $$b{COUNT} <=> $$a{COUNT}
                          ||
              $$a{DATA}  cmp $$b{DATA}
           }
      map  {
             +{
                  COUNT  => tr/MATCH//,
                  DATA   => $_,
              }
      } @allmatches;

Видите, насколько это намного лучше?Кроме того, когда вы читаете его снизу вверх, он соответствует потоку данных в стиле оболочки, который предельно прост:

  map @allmatches | sort | map | print

Что гораздо проще понять, чем

  print(map(sort(map @allmatches)))

и это причина, по которой все предпочитают модель потока данных оболочки.

3 голосов
/ 29 мая 2011

Ой, а также и гадость ...

print map $_->[1], 
            sort {
          $a->[0] cmp $b->[0] ##first element of the array
          or $a->[1] cmp $b->[1] } 
      map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches;

Часть sort относительно прямолинейна.

sort { $a->[0] cmp $b->[0] or $a->[1] cmp $b->[1] } ...an array...

Каждый элемент массива сам является массивом refи сравнение выполняет сравнение строк (cmp) первых элементов массива refs, и, если они равны (cmp возвращает 0), вторых элементов.

Выходные данные отсортированымассив, следовательно.Это оставляет два куска кода для анализа.Первая строка и последняя строка.Последняя строка запускает map:

map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches

Это, очевидно, выполняет преобразование без операции, поскольку левая и правая строки в операторе tr/// одинаковы;это немного озадачивает.[ Обновление : tr/// подсчитывает, сколько раз каждая из букв MATCH появляется в строке;внутри 'block' или 'expr' в map, $_ есть специальная переменная - сопоставляемое значение.] Но он берет каждый элемент @allmatches, отображает его и выводитиз этого передается в род.Квадратные скобки образуют массив ref, поэтому на выходе получается массив ссылок ref;каждая ссылка на массив содержит количество букв из слова MATCH в слове, за которым следует слово.

Первая строка:

print map $_->[1], ...output from sort...;

Это извлекает имя $_->[1]из отсортированного вывода.

  • В целом, эффект состоит в том, чтобы перечислить слова в @allmatches в таком порядке, чтобы слова с наименьшим (возможно, нулевым) буквой из MATCH появлялись первыми в алфавитном порядке.за ними следуют те, у которых следующие наименьшие буквы из MATCH (снова в алфавитном порядке) и т. д.

Это сжатие tour de force . Если бы кто-то предоставил мне его для обзора, он бы вернулся к чертежной доске. ( Обновление : так как это известная идиома ( преобразование Шварца * 1045)*), единственные причины для его отправки - «недостаточно тщательно изложены» и «не аннотированы как преобразование Шварца».)

# Schwartzian Transform: sort by number of letters from MATCH and alphabetically
print map  { $_->[1] } 
      sort { $a->[0] <=> $b->[0] or $a->[1] cmp $b->[1] } 
      map  { [ tr/"MATCH"/"MATCH"/, $_ ] }
      @allmatches;

(Это правильно использует числовое сравнение для первого термина.)

Вы упоминаете, что были озадачены $a и $b.В основном это магические переменные - параметры функции сравнения в сортировке.Сравнение должно возвращать отрицательное значение, если $a сравнивает меньше $b, или положительное, если $a сравнивает больше $b, или ноль, если они сравниваются равными.Они ($a и $b) являются именами, используемыми, когда требуются два имени;$_ используется с mapgrep и другими функциями преобразования списка), где требуется только одно имя.

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