TeX, 216 байт (4 строки по 54 символа в каждой)
Потому что речь идет не о количестве байтов, а о качестве вывода на наборе :-)
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
Попробуйте онлайн! (На обороте; не уверен, как это работает)
Полный тестовый файл:
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\bye
Выход:
Для LaTeX вам просто нужен шаблон:
\documentclass{article}
\begin{document}
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\end{document}
объяснение
TeX странный зверь. Чтение нормального кода и его понимание само по себе является подвигом. Понимание запутанного кода TeX делает несколько шагов дальше. Я постараюсь сделать это понятным для людей, которые также не знают TeX, поэтому перед тем, как мы начнем, вот несколько понятий о TeX, чтобы упростить понимание:
Для (не очень) абсолютных новичков TeX
Первый и самый важный пункт в этом списке: код не обязательно должен быть в форме прямоугольника, хотя поп-культура может заставить вас так думать .
TeX - это язык расширения макросов. В качестве примера вы можете определить, \def\sayhello#1{Hello, #1!}
а затем написать, \sayhello{Code Golfists}
чтобы заставить TeX печатать Hello, Code Golfists!
. Это называется «неопределенным макросом», и для подачи его в первый (и единственный, в данном случае) параметр вы заключаете его в фигурные скобки. TeX удаляет эти скобки, когда макрос захватывает аргумент. Вы можете использовать до 9 параметров: \def\say#1#2{#1, #2!}
затем \say{Good news}{everyone}
.
Аналог неделимитированных макросов, что не удивительно, разделители из них :) Вы можете сделать предыдущее определение чуть более семантическим : \def\say #1 to #2.{#1, #2!}
. В этом случае за параметрами следует так называемый текст параметра . Такой параметр text ограничивает аргумент макроса ( #1
разграничивается ␣to␣
, включая пробелы, и #2
разграничивается .
). После этого определения вы можете написать \say Good news to everyone.
, что будет расширяться до Good news, everyone!
. Хорошо, не правда ли? :) Однако разделители аргумент (цитируя . Произведет странное предложение В этом случае вам нужно «скрыть» первый с : . TeXbook ) «самая короткая (возможно, пустая) последовательность токенов с правильно вложенными {...}
группами, за которой следует ввод этого конкретного списка непараметрических токенов». Это означает, что расширение\say Let's go to the mall to Martin
␣to␣
{...}
\say {Let's go to the mall} to Martin
Все идет нормально. Теперь все становится странным. Когда TeX читает символ (который определяется «кодом символа»), он назначает этому символу «код категории» (catcode, для друзей :), который определяет, что этот символ будет означать. Эта комбинация символа и кода категории создает токен (подробнее об этом здесь , например). Вот те, которые представляют для нас интерес:
catcode 11 , который определяет токены, которые могут составлять управляющую последовательность (шикарное имя для макроса). По умолчанию все буквы [a-zA-Z] являются катодом 11, поэтому я могу написать \hello
, что является одной последовательностью управления, в то время \he11o
как последовательность управления \he
сопровождается двумя символами 1
, за которыми следует буква o
, потому что 1
это не катод 11. Если я сделал \catcode`1=11
, с этого момента \he11o
будет одна контрольная последовательность. Одна важная вещь заключается в том, что катокоды устанавливаются, когда TeX впервые видит символ под рукой, и такой каткод замораживается ... НАВСЕГДА! (условия могут применяться)
catcode 12 , которые являются большинством других символов, таких как 0"!@*(?,.-+/
и так далее. Они являются наименее специальным типом кат-кода, поскольку они служат только для написания материала на бумаге. Но кто же использует TeX для записи?!? (опять же, условия могут применяться)
Catcode 13 , который ад :) Действительно. Хватит читать и иди делай что-нибудь из своей жизни. Вы не хотите знать, что такое catcode 13. Вы когда-нибудь слышали о пятницу 13-го? Угадай, откуда он получил свое имя! Продолжайте на свой страх и риск! Символ catcode 13, также называемый «активным» символом, больше не просто символ, а сам макрос! Вы можете определить его так, чтобы он имел параметры и расширился до того, что мы видели выше. После того, как \catcode`e=13
вы думаете, вы можете сделать \def e{I am the letter e!}
, НО. ТЫ. НЕ МОГУТ! e
это уже не письмо, так \def
что не \def
вы знаете, это так \d e f
! О, выбери еще одну букву? Хорошо! \catcode`R=13 \def R{I am an ARRR!}
, Хорошо, Джимми, попробуй! Смею вас сделать это и написать R
в своем коде! Вот что такое catcode 13. Я СПОКОЕН! Давайте двигаться дальше.
Хорошо, теперь группировка. Это довольно просто. Независимо от того, какие назначения ( \def
это операция присваивания, \let
(мы будем в нее входить), это другое), выполненные в группе, восстанавливаются до того, чем они были до того, как эта группа была запущена, если это назначение не является глобальным. Существует несколько способов запуска групп, один из которых - с кодами catcode 1 и 2 (опять же, catcodes). По умолчанию {
это catcode 1, или begin-group, и }
это catcode 2, или end-group. Пример: \def\a{1} \a{\def\a{2} \a} \a
это печатает 1 2 1
. Снаружи группа \a
была 1
, затем внутри она была переопределена 2
, и когда группа закончилась, она была восстановлена 1
.
Эта \let
операция похожа на другую операцию присваивания \def
, но отличается от нее. С \def
вами вы определяете макросы, которые будут расширяться, а \let
вы создаете копии уже существующих вещей. После \let\blub=\def
( =
опционально) вы можете изменить начало e
примера с элемента catcode 13, приведенного выше, \blub e{...
и повеселиться с ним. Или лучше, вместо того , чтобы ломать вещи можно исправить (вы посмотрите на это!) На R
пример: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
. Быстрый вопрос: не могли бы вы переименовать в \newR
?
Наконец, так называемые «паразитные пространства». Это своего рода запретная тема, потому что есть люди, которые утверждают, что репутация, заработанная в TeX - LaTeX Stack Exchange , отвечая на вопросы о «ложных местах», не должна рассматриваться, в то время как другие искренне не согласны. С кем вы согласны? Делайте ваши ставки! Между тем: TeX понимает разрыв строки как пробел. Попробуйте написать несколько слов с разрывом строки (не пустой строкой ) между ними. Теперь добавьте %
в конце этих строк. Как будто вы «закомментировали» эти пробелы в конце строки. Это оно :)
(Вроде) разглаживание кода
Давайте сделаем этот прямоугольник во что-то (возможно) более понятным:
{
\let~\catcode
~`A13
\defA#1{~`#113\gdef}
AGG#1{~`#113\global\let}
GFF\else
GHH\fi
AQQ{Q}
AII{\ifxQ}
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
ADD#1#2#3|{I#2FE{#1}#2#3|H}
ACC#1#2|{D{}#2Q|#1 }
ABBH#1 {HI#1FC#1|BH}
\gdef\S#1{\iftrueBH#1 Q }
}
Объяснение каждого шага
каждая строка содержит одну единственную инструкцию. Давайте пойдем один за другим, разбирая их:
{
Сначала мы запускаем группу, чтобы некоторые изменения (а именно, изменения кода) были локальными, чтобы они не испортили введенный текст.
\let~\catcode
В основном все коды запутывания TeX начинаются с этой инструкции. По умолчанию, как в обычном TeX, так и в LaTeX, этот ~
символ является одним активным символом, который можно преобразовать в макрос для дальнейшего использования. И лучший инструмент для странного кодирования TeX - это изменение кода, так что это, как правило, лучший выбор. Теперь вместо \catcode`A=13
мы можем написать ~`A13
( =
необязательно):
~`A13
Теперь буква A
является активным символом, и мы можем определить, что он делает:
\defA#1{~`#113\gdef}
A
теперь макрос, который принимает один аргумент (который должен быть другим символом). Сначала catcode аргумента изменяется на 13 , чтобы сделать его активным: ~`#113
(заменить ~
на \catcode
и добавить =
и у вас есть: \catcode`#1=13
). Наконец, он оставляет \gdef
(глобальный \def
) во входном потоке. Короче говоря, A
активизирует другого персонажа и начинает его определение. Давай попробуем:
AGG#1{~`#113\global\let}
AG
Сначала «активирует» G
и делает \gdef
, после чего следует следующее G
запускает определение. Определение G
очень схоже с определением A
, за исключением того, что вместо \gdef
него \global\let
(а не \glet
как \gdef
). Короче говоря, G
активирует персонажа и делает его чем-то другим. Давайте сделаем ярлыки для двух команд, которые мы будем использовать позже:
GFF\else
GHH\fi
Теперь вместо \else
и \fi
мы можем просто использовать F
и H
. Короче :)
AQQ{Q}
Теперь мы используем A
снова для определения другого макроса Q
. Вышеупомянутое утверждение в основном делает (на менее запутанном языке) \def\Q{\Q}
. Это не очень интересное определение, но у него есть интересная особенность. Если вы не хотите нарушать какой-либо код, единственный макрос, который расширяется, Q
это Q
сам по себе, поэтому он действует как уникальный маркер (он называется кварком ). Вы можете использовать \ifx
условное выражение для проверки, является ли аргумент макроса таким кварком с \ifx Q#1
:
AII{\ifxQ}
так что вы можете быть уверены, что нашли такой маркер. Обратите внимание, что в этом определении я удалил пробел между \ifx
и Q
. Обычно это приводит к ошибке (обратите внимание, что выделение синтаксиса думает, что \ifxQ
это одно), но так как теперь Q
это код 13, он не может сформировать управляющую последовательность. Однако будьте осторожны, чтобы не расширять этот кварк, иначе вы застрянете в бесконечном цикле, потому что Q
расширяется в Q
который расширяется в Q
какой ...
Теперь, когда предварительные выводы завершены, мы можем перейти к правильному алгоритму для установки. Из-за токенизации TeX алгоритм должен быть написан задом наперед. Это связано с тем, что в то время, когда вы выполняете определение, TeX будет токенизировать (присваивать катокоды) символам в определении, используя текущие настройки, например, если я это сделаю:
\def\one{E}
\catcode`E=13\def E{1}
\one E
выходной E1
, тогда как, если я изменю порядок определений:
\catcode`E=13\def E{1}
\def\one{E}
\one E
выход есть 11
. Это связано с тем, что в первом примере E
в определении был токенизирован как буква (catcode 11) перед изменением catcode, так что это всегда будет букваE
. Во втором примере, однако, E
сначала был сделан активным, и только потом \one
был определен, и теперь определение содержит код 13, E
который расширяется до1
.
Я, однако, пропущу этот факт и переупорядочу определения, чтобы иметь логический (но не работающий) порядок. В следующих параграфах вы можете предположить, что буквы B
,C
, D
и E
являются активными.
\gdef\S#1{\iftrueBH#1 Q }
(обратите внимание, что в предыдущей версии была небольшая ошибка, она не содержала заключительного пробела в приведенном выше определении. Я заметил это только во время написания этого. Читайте дальше, и вы поймете, зачем нам это нужно для правильного завершения макроса. )
Сначала мы определим макрос уровня пользователя \S
. Этот не должен быть активным символом, чтобы иметь дружественный (?) Синтаксис, поэтому макрос для gwappins eht setterl есть \S
. Макрос начинается с всегда истинного условия \iftrue
(скоро станет понятно, почему), а затем вызывает B
макрос, за которым следует H
(который мы определили ранее \fi
) для соответствия \iftrue
. Затем мы оставляем аргумент макроса, #1
за которым следует пробел и кварк Q
. Предположим, мы используем \S{hello world}
, то входной поток должны выглядеть следующим образом:\iftrue BHhello world Q␣
(Я заменил последний пробел на ␣
так, чтобы рендеринг сайта его не съел, как я делал в предыдущей версии кода). \iftrue
это правда, поэтому он расширяется, и мы остались с не расширяется. Теперь макрос расширен:BHhello world Q␣
, TeX делает удалить \fi
( H
) после того , как условно оценивается, вместо этого она оставляет его там до тех пор , \fi
пока на самом делеB
ABBH#1 {HI#1FC#1|BH}
B
является макросом с разделителями, чей параметр text является H#1␣
, поэтому аргумент - это то, что находится между H
и пробелом. Продолжая приведенный выше пример входного потока перед расширением B
is BHhello world Q␣
. B
затем следует H
, как и должно (в противном случае TeX вызовет ошибку), затем следующий пробел между hello
и world
, #1
как и слово hello
. И здесь мы должны разделить входной текст по пробелам. Ура: D экспансия . Последний пробел после необходим, потому что макрос с разделителями требуетB
удаляет все вплоть до первого места из входного потока и заменяет на HI#1FC#1|BH
с #1
того hello
: HIhelloFChello|BHworld Q␣
. Обратите внимание, что BH
позже во входном потоке есть новое , чтобы выполнить хвостовую рекурсиюB
и обработать более поздние слова. После обработки этого слова обрабатывается B
следующее слово, пока обрабатываемое слово не станет кварком один в конце аргумента. В предыдущей версии (см. Историю изменений) код будет некорректно работать, если вы используете (пробел между s исчезнет).Q
Q
B
\S{hello world}abc abc
abc
OK, обратно во входной поток: HIhelloFChello|BHworld Q␣
. Сначала есть H
( \fi
), который завершает начальную \iftrue
. Теперь у нас есть это (псевдокодирование):
I
hello
F
Chello|B
H
world Q␣
I...F...H
Думают на самом деле \ifx Q...\else...\fi
структура. В \ifx
тест проверяет , является ли (первый токен) слово схватил является Q
кварк. Если нет ничего другого, и выполнение завершается, в противном случае что остается: Chello|BHworld Q␣
. В настоящее времяC
расширено:
ACC#1#2|{D#2Q|#1 }
Первый аргумент C
является не выделяются, так что если не приготовился будет один маркер, второй аргумент ограничен |
, так что после расширения C
(с #1=h
и #2=ello
) входной поток: DelloQ|h BHworld Q␣
. Обратите внимание , что другой |
ставятся там, и h
из hello
ставятся после этого. Половина обмена сделана; первая буква в конце. В TeX легко получить первый токен из списка токенов. Простой макрос \def\first#1#2|{#1}
получает первую букву при использовании \first hello|
. Последний является проблемой, потому что TeX всегда берет «самый маленький, возможно, пустой» список токенов в качестве аргумента, поэтому нам нужно несколько обходных путей. Следующий элемент в списке токенов D
:
ADD#1#2|{I#1FE{}#1#2|H}
Этот D
макрос является одним из обходных путей и полезен в единственном случае, когда слово имеет одну букву. Предположим, вместо hello
нас x
. В этом случае входной поток будет DQ|x
, то D
будет расширяться (с #1=Q
, и #2
опорожнить) к: IQFE{}Q|Hx
. Это похоже на блок I...F...H
( \ifx Q...\else...\fi
) в B
, который увидит, что аргумент является кварком, и прервет выполнение, оставив только x
набор текста. В других случаях (возвращение к hello
примеру), D
будет расширяться (с #1=e
и #2=lloQ
) по адресу: IeFE{}elloQ|Hh BHworld Q␣
. Опять же , I...F...H
будет проверять , Q
но потерпит неудачу и взять \else
власть: E{}elloQ|Hh BHworld Q␣
. Теперь последний кусок этой вещи,E
макрос будет расширяться:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
Текст параметра здесь очень похож на C
и D
; первый и второй аргументы не ограничены, а последний ограничен |
. Входной поток выглядит следующим образом : E{}elloQ|Hh BHworld Q␣
, затем E
расширяется (с #1
пустым, #2=e
, и #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Другой I...F...H
блок проверяет кварк (который видит l
и возвращается false
): E{e}lloQ|HHh BHworld Q␣
. Теперь E
снова расширяется (с #1=e
пустым, #2=l
и #3=loQ
): IloQleFE{el}loQ|HHHh BHworld Q␣
. И снова I...F...H
. Макрос делает еще несколько итераций, пока, Q
наконец, не будет найден и true
ветвь не берется: E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> IQoellFE{ello}Q|HHHHHh BHworld Q␣
. Теперь кварк найден и условные Раскрывается в: oellHHHHh BHworld Q␣
. Уф.
Ой, подожди, что это? НОРМАЛЬНЫЕ ПИСЬМА? О, парень! Письма , наконец , нашли и TeX запишет oell
, то связка H
( \fi
) будут найдены и расширены (ничего) , выходящий из входного потока с: oellh BHworld Q␣
. Теперь в первом слове поменялись местами первая и последняя буквы, а TeX находит следующее, B
чтобы повторить весь процесс для следующего слова.
}
Наконец мы заканчиваем группу, начатую там, так что все локальные назначения отменяются. Местные назначения являются catcode изменения букв A
, B
, C
, ... , которые были сделаны макросы так , что они возвращаются к своему нормальному значению буквы и может безопасно использоваться в тексте. Вот и все. Теперь \S
определенный здесь макрос запустит обработку текста, как указано выше.
Одна интересная вещь об этом коде - то, что он полностью расширяем. То есть вы можете безопасно использовать его в движущихся аргументах, не опасаясь, что он взорвется. Вы даже можете использовать код, чтобы проверить, совпадает ли последняя буква слова со второй (по какой бы причине вам это не понадобилось) в \if
тесте:
\if\S{here} true\else false\fi % prints true (plus junk, which you would need to handle)
\if\S{test} true\else false\fi % prints false
Извините за (вероятно, слишком) многословное объяснение. Я постарался сделать это как можно более понятным для не TeXies :)
Резюме для нетерпеливых
Макрос \S
добавляет к вводу активный символ, B
который захватывает списки токенов, разделенных конечным пробелом, и передает их C
. C
берет первый токен в этом списке и перемещает его в конец списка токенов и расширяет D
с тем, что осталось. D
проверяет, является ли «то, что осталось» пустым, и в этом случае было найдено однобуквенное слово, затем ничего не делает; в противном случае расширяется E
. E
циклически перебирает список токенов до тех пор, пока не найдет последнюю букву в слове, когда он найден, он покидает последнюю букву, за которой следует середина слова, за которой следует первая буква, оставленная в конце потока токенов: C
,
Hello, world!
становится,elloH !orldw
(заменяя пунктуацию буквой) илиoellH, dorlw!
(сохраняя пунктуацию на месте)?