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и пробелом. Продолжая приведенный выше пример входного потока перед расширением Bis 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 исчезнет).QQB \S{hello world}abc abcabc
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!(сохраняя пунктуацию на месте)?