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

#perl

#perl

Вопрос:

Я читал руководства и 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; 
  

Итак, что мне особенно нужно: на что ссылается $ _ (не определено?)

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

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

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

Вот моя идея:
отображает неопределенный скаляр в качестве ссылки на массив (какой?) одновременный вызов второго элемента: [1]. Он сортирует мой массив @allmatches сначала по количеству вхождений «СОВПАДЕНИЙ», а затем по алфавиту. Вторая карта путем создания ссылки является грубой для меня (карты многое делают за один шаг); tr возвращает количество раз. Второе «СОВПАДЕНИЕ» бесполезно, но почему?

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

Ответ №1:

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

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

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

tr/"MATCH"/"MATCH"/ подсчитывает, сколько раз буквы M, 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 идиома называется преобразованием Шварца.]

Комментарии:

1. Неплохо… Я думаю, что квадратная скобка выдает ссылки на массивы, а не массивы, но анализ tr/// лучше моего. И вы также лучше справляетесь с последней операцией отображения. Молодец.

2. @Jonathan: Вы правы. Я отредактировал свой ответ, чтобы быть более точным. Спасибо.

3. 1 за объяснение. Я думаю, вы правы; это ошибка. cmp преобразует целочисленные значения в строки, поэтому while 10 <=> 2 возвращает 1 , 10 cmp 2 возвращает -1 (потому что ’10’ вместо ‘2’).

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

5. Объяснение правильное, но вы должны также упомянуть, что эта идиома ( map sort map ) называется преобразованием Шварца и используется для эффективной сортировки массива по ключу, вычисление которого может быть дорогостоящим.

Ответ №2:

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

 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)))
  

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

Комментарии:

1. Спасибо за некоторые конкретные изменения, которые я могу реализовать, и за использование этих ярлыков для очистки. Теперь я думаю, что могу заменить tr /// циклом и регулярным выражением для лучшей сортировки по частоте. Спасибо!

Ответ №3:

Ой, и аналогично фу…

 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...
  

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

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

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

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

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

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

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

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

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

 # 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 ) являются именами, используемыми, когда требуются два имени; $_ используется с map grep и другими функциями преобразования списка), где требуется только одно имя.

Комментарии:

1. 1. У вас есть в основном тот же ответ, который я пытался составить, но я также был озадачен использованием tr :). Я бы также упомянул, что $_ также является специальной переменной и не является неопределенным

2. Большое спасибо за подробное решение. многое прояснилось, массив ссылок на массивы, я думаю, меня запутал.

3. tr возвращает значение int. Опасно вызывать cmp это вместо <=> .

4. @tchrist, я не думаю, что «опасный» — правильное слово; он просто не делает то, что вы хотите (если только ваши требования не действительно странные).

5. @cjm: Ну, "2" после "11" будет сортировка, что вряд ли приведет к приятному результату. Смотрите мой ответ.