!rm`.*$
Retina работает в режиме Match с одним регулярным выражением. Обычно это просто печатает количество совпадений, но при этом !
мы настраиваем его для печати реальных совпадений (разделенных переводом строки).
Фактическое регулярное выражение просто .*$
. .*
соответствует любой строке (потенциально пустой), потому что .
может соответствовать любому символу, кроме перевода строки. Я вернусь $
через минуту.
Как мы заставим его печатать спички в обратном порядке? Используя .NET режим сопоставления справа налево, активируется с помощью r
. Это означает, что механизм регулярных выражений запускается в конце строки при поиске совпадений и работает в обратном направлении.
И, наконец, m
делает $
матч конец строки , а не в конце строки. Зачем нам это вообще нужно? Беда в том, что .*
генерирует посторонние совпадения. Рассмотрим подстановку регулярных выражений
s/a*/$0x/
применяется к входу baaababaa
. Вы могли бы подумать, что это даст baaaxbaxbaax
, но на самом деле это дает вам baaaxxbaxxbaaxx
. Зачем? Потому что после сопоставления aaa
курсор двигателя находится между a
и b
. Теперь он не может больше соответствовать a
s, но a*
также удовлетворен пустой строкой. Это означает, что после каждого совпадения вы получаете еще одно пустое совпадение.
Мы не хотим этого здесь, потому что это введет дополнительные пустые строки, поэтому мы отбрасываем эти посторонние совпадения (которые находятся в начале строк, из-за режима справа налево), требуя, чтобы совпадения включали конец линия.
tac
немного странно, когда речь заходит о переводе строки. Он преобразуетсяa\nb\n
(завершающий перевод строки) вb\na\n
иa\nb
(без завершающего перевода строки) вba\n
. Это то, как наш код должен себя вести?