TeX, 216 bajtów (4 linie, po 54 znaki każda)
Ponieważ nie chodzi o liczbę bajtów, chodzi o jakość wydruku składu :-)
{\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 }}
Wypróbuj online! (Na odwrocie; nie jestem pewien, jak to działa)
Pełny plik testowy:
{\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
Wydajność:

Do LaTeX potrzebujesz tylko płyty grzewczej:
\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}
Wyjaśnienie
TeX to dziwna bestia. Czytanie normalnego kodu i rozumienie go jest wyczynem samym w sobie. Zrozumienie zaciemnionego kodu TeX idzie o kilka kroków dalej. Spróbuję uczynić to zrozumiałym dla osób, które również nie znają TeXa, więc zanim zaczniemy, oto kilka pomysłów na temat TeXa, aby ułatwić śledzenie:
Dla (nie) absolutnych początkujących TeX-ów
Pierwszy i najważniejszy element tej listy: kod ma nie być w kształcie prostokąta, choć popkultura może prowadzić cię tak myśleć .
TeX to język rozwijania makr. Możesz na przykład zdefiniować, \def\sayhello#1{Hello, #1!}a następnie napisać, \sayhello{Code Golfists}aby TeX wydrukował Hello, Code Golfists!. Nazywa się to „nieelimitowanym makrem” i aby podać mu pierwszy (i tylko w tym przypadku) parametr, umieść go w nawiasach klamrowych. TeX usuwa te nawiasy klamrowe, gdy makro pobiera argument. Możesz użyć do 9 parametrów: \def\say#1#2{#1, #2!}wtedy \say{Good news}{everyone}.
Odpowiednikiem undelimited makra są, nic dziwnego, rozdzielany te :) Można zrobić poprzednia definicja odrobinę bardziej semantyczne : \def\say #1 to #2.{#1, #2!}. W takim przypadku po parametrach następuje tak zwany tekst parametru . Taki tekst parametru ogranicza argument makra ( #1jest ograniczony przez ␣to␣spacje i #2jest ograniczony przez .). Po tej definicji możesz napisać \say Good news to everyone., która rozwinie się do Good news, everyone!. Fajnie, prawda? :) Jednak ograniczonym argumentem jest (cytując TeXbooka ) „najkrótszą (być może pustą) sekwencję tokenów z odpowiednio zagnieżdżonymi {...}grupami, po której następuje na wejściu ta konkretna lista tokenów nieparametrycznych”. Oznacza to, że rozszerzenie\say Let's go to the mall to Martinwyda dziwne zdanie. W tym przypadku trzeba by „ukryć” Pierwszy ␣to␣z {...}: \say {Let's go to the mall} to Martin.
Jak na razie dobrze. Teraz zaczyna się robić dziwnie. Kiedy TeX odczytuje znak (który jest zdefiniowany przez „kod znaku”), przypisuje temu znakowi „kod kategorii” (catcode, dla znajomych :), który określa, co ten znak będzie oznaczał. Ta kombinacja znaków i kod kategorii sprawia tokena (więcej na ten temat tutaj , na przykład). Te, które są dla nas tutaj interesujące, to w zasadzie:
catcode 11 , który definiuje tokeny, które mogą tworzyć sekwencję kontrolną (szykowna nazwa makra). Domyślnie wszystkie litery [a-zA-Z] są kodem kat. 11, więc mogę napisać \hello, że jest to jedna pojedyncza sekwencja kontrolna, podczas \he11ogdy sekwencja kontrolna, \hepo której następują dwa znaki 1, a następnie litera o, ponieważ 1nie jest to kod kat. 11. Jeśli I tak \catcode`1=11, od tego momentu \he11obędzie jedna sekwencja kontrolna. Jedną ważną rzeczą jest to, że kody te są ustawiane, gdy TeX po raz pierwszy widzi postać pod ręką, a taki kod jest zamrożony ... NA ZAWSZE! (mogą obowiązywać warunki)
catcode 12 , które są większością innych znaków, takich jak 0"!@*(?,.-+/i tak dalej. Są najmniej specjalnym typem kodu, ponieważ służą tylko do pisania rzeczy na papierze. Ale hej, kto używa TeXa do pisania?!? (ponownie mogą obowiązywać warunki)
catcode 13 , co jest piekłem :) Naprawdę. Przestań czytać i zrób coś ze swojego życia. Nie chcesz wiedzieć, co to jest catcode 13. Słyszałeś kiedyś o piątku, 13? Zgadnij, skąd wzięła się jego nazwa! Kontynuuj na własne ryzyko! Znak catcode 13, zwany również „aktywnym” znakiem, nie jest już tylko znakiem, to samo makro! Możesz to zdefiniować, aby mieć parametry i rozwinąć do czegoś takiego, jak widzieliśmy powyżej. Po tym \catcode`e=13, jak myślisz, że możesz \def e{I am the letter e!}, ALE. TY. NIE MOŻE! enie jest już listem, więc \defnie \defwiesz, to jest \d e f! Och, wybierz inny list, który mówisz? W porządku! \catcode`R=13 \def R{I am an ARRR!}. Bardzo dobrze, Jimmy, spróbuj! Odważę się to zrobić i napisać Rw kodzie! To właśnie jest catcode 13. JESTEM SPOKOJNY! Przejdźmy dalej.
OK, teraz do grupowania. Jest to dość proste. Jakiekolwiek przypisania ( \defjest to operacja przypisania \let(przejdziemy do niej)) jest inna) wykonywane w grupie, są przywracane do stanu sprzed rozpoczęcia grupy, chyba że przypisanie ma charakter globalny. Istnieje kilka sposobów zakładania grup, jeden z nich zawiera kod 1 i 2 znaki (och, znowu kody). Domyślnie {jest to catcode 1 lub grupa }początkowa , a catcode 2 lub grupa końcowa. Przykład: \def\a{1} \a{\def\a{2} \a} \ato drukuje 1 2 1. Na zewnątrz \abyła grupa 1, potem wewnątrz została na nowo zdefiniowana 2, a kiedy grupa się zakończyła, została przywrócona 1.
Ta \letoperacja jest kolejną operacją przypisania \def, ale raczej inną. Z \defty definiować makra, które rozwinie się rzeczy, o \letutworzeniu kopii już istniejących rzeczy. Po \let\blub=\def( =opcja jest opcjonalna) możesz zmienić początek eprzykładu z powyższego elementu catcode 13 na \blub e{...i bawić się z nim. Albo lepiej, zamiast zerwania rzeczy można naprawić (by spojrzeć na to!) Na Rprzykład: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}. Szybkie pytanie: czy możesz zmienić nazwę \newR?
Wreszcie tak zwane „przestrzenie fałszywe”. Jest to rodzaj tematu tabu, ponieważ są ludzie, którzy twierdzą, że reputacja zdobyta w TeX - LaTeX Stack Exchange , odpowiadając na pytania „fałszywych spacji”, nie powinna być brana pod uwagę, podczas gdy inni całkowicie się nie zgadzają. Z kim się zgadzasz? Postaw zakłady! Tymczasem: TeX rozumie podział linii jako spację. Spróbuj napisać kilka słów z podziałem linii (nie pustą linią ) między nimi. Teraz dodaj %na końcu tych wierszy. To tak, jakbyś „komentował” te przestrzenie na końcu linii. To jest to :)
(Trochę) odhaczanie kodu
Zróbmy z tego prostokąta coś (prawdopodobnie) łatwiejszego do naśladowania:
{
\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 }
}
Objaśnienie każdego kroku
każda linia zawiera jedną instrukcję. Chodźmy jeden po drugim, analizując je:
{
Najpierw zakładamy grupę, aby niektóre zmiany (mianowicie zmiany kodu źródłowego) były lokalne, aby nie zakłócały tekstu wejściowego.
\let~\catcode
Zasadniczo wszystkie kody zaciemniania TeX-a zaczynają się od tej instrukcji. Domyślnie, zarówno w zwykłym TeX-ie, jak i LaTeX-u, ~znak jest jedną aktywną postacią, którą można przekształcić w makro do dalszego wykorzystania. A najlepszym narzędziem do dziwnego kodowania TeXa są zmiany kodu cat, więc ogólnie jest to najlepszy wybór. Teraz zamiast \catcode`A=13możemy napisać ~`A13( =jest opcjonalne):
~`A13
Teraz litera Ajest aktywną postacią i możemy ją zdefiniować, aby coś zrobić:
\defA#1{~`#113\gdef}
Ajest teraz makrem, które przyjmuje jeden argument (który powinien być innym znakiem). Najpierw kotokodu argumentu zostaje zmieniona na 13, aby ją uaktywnić: ~`#113(zastąpienie ~przez \catcodei dodaj =i masz: \catcode`#1=13). Wreszcie pozostawia \gdef(globalny \def) w strumieniu wejściowym. Krótko mówiąc, Aaktywuje inną postać i rozpoczyna jej definicję. Spróbujmy:
AGG#1{~`#113\global\let}
AGpierwsza „aktywuje” Gi robi \gdef, a następnie następna Grozpoczyna definicję. Definicja Gjest bardzo podobna do tej A, z tą różnicą, że zamiast \gdefniej robi \global\let(nie ma takiej \gletjak \gdef). Krótko mówiąc, Gaktywuje postać i sprawia, że staje się czymś innym. Zróbmy skróty do dwóch poleceń, których użyjemy później:
GFF\else
GHH\fi
Teraz zamiast \elsei \fimożemy po prostu użyć Fi H. Znacznie krótszy :)
AQQ{Q}
Teraz używamy Aponownie zdefiniować inny makra Q. Powyższe stwierdzenie w zasadzie ma (w mniej zaciemnionym języku) \def\Q{\Q}. To nie jest strasznie interesująca definicja, ale ma ciekawą funkcję. Jeśli nie chcesz złamać jakiegoś kodu, jedynym makrem, które się rozwija, Qjest Qsamo, więc działa jak unikalny znacznik (nazywa się kwark ). Możesz użyć \ifxwarunkowego do sprawdzenia, czy argumentem makra jest taki kwark z \ifx Q#1:
AII{\ifxQ}
więc możesz być całkiem pewien, że znalazłeś taki znacznik. Zauważ, że w tej definicji usunąłem spację między \ifxi Q. Zwykle prowadziłoby to do błędu (zwróć uwagę, że podświetlanie składni uważa, że \ifxQto jedno), ale ponieważ teraz Qjest catcode 13, nie może utworzyć sekwencji kontrolnej. Uważaj jednak, aby nie rozwinąć tego kwarka, w przeciwnym razie utkniesz w nieskończonej pętli, ponieważ Qrozwija się do Qktórego rozwija się do Qktórego ...
Po zakończeniu wstępnych przygotowań możemy przejść do właściwego algorytmu pwas eht setterl. Z powodu tokenizacji TeX-a algorytm musi być zapisany wstecz. Wynika to z tego, że w momencie tworzenia definicji TeX będzie tokenizował (przypisywał kody) do znaków w definicji przy użyciu bieżących ustawień, na przykład, jeśli:
\def\one{E}
\catcode`E=13\def E{1}
\one E
dane wyjściowe są E1, natomiast jeśli zmienię kolejność definicji:
\catcode`E=13\def E{1}
\def\one{E}
\one E
wyjście jest 11. Jest tak, ponieważ w pierwszym przykładzie Edefinicja była tokenizowana jako litera (kod 11) przed zmianą kodu, więc zawsze będzie to litera E. W drugim przykładzie Ezostał jednak najpierw uaktywniony, a dopiero potem \onezostał zdefiniowany, a teraz definicja zawiera kod kat. 13, Ektóry rozwija się 1.
Przeoczę jednak ten fakt i zmienię kolejność definicji, aby uzyskać logiczną (ale nie działającą) kolejność. W poniższych akapitach można założyć, że litery B, C, D, i Esą aktywne.
\gdef\S#1{\iftrueBH#1 Q }
(zauważ, że w poprzedniej wersji był mały błąd, nie zawierał on końcowego miejsca w powyższej definicji. Zauważyłem to tylko podczas pisania tego. Czytaj dalej, a zobaczysz, dlaczego potrzebujemy tego, aby poprawnie zakończyć makro. )
Najpierw musimy zdefiniować makro poziomie użytkownika \S. Ten nie powinien być aktywną postacią, aby mieć przyjazną (?) Składnię, więc makro dla gwappins eht setterl to \S. Makro zaczyna się od zawsze prawdziwego warunku \iftrue(wkrótce będzie jasne, dlaczego), a następnie wywołuje Bmakro, po Hktórym następuje (które wcześniej zdefiniowaliśmy \fi) w celu dopasowania do \iftrue. Następnie pozostawiamy argument makra, #1po którym następuje spacja i kwark Q. Załóżmy, że używamy \S{hello world}, a następnie strumienia wejściowegopowinien wyglądać tak: \iftrue BHhello world Q␣(ostatnie miejsce zastąpiłem ␣tak, aby renderowanie strony go nie zjadło, tak jak w poprzedniej wersji kodu). \iftruejest prawdą, więc się rozszerza i zostajemy z tym BHhello world Q␣. TeX czy nie usunąć \fi( H) po warunkowe ocenia się, zamiast tego pozostawia go tam, dopóki \fijest faktycznie rozszerzony. Teraz Bmakro jest rozwinięte:
ABBH#1 {HI#1FC#1|BH}
Bjest ograniczonym makrem, którego tekstem parametru jest H#1␣, więc argumentem jest cokolwiek pomiędzy Hspacją. Kontynuując przykład powyżej strumienia wejściowego przed rozszerzeniem Bjest BHhello world Q␣. Bpo nim następuje H, tak jak powinien (w przeciwnym razie TeX spowodowałby błąd), następnie następna spacja znajduje się pomiędzy helloi world, podobnie #1jak słowo hello. I tutaj musimy podzielić tekst wejściowy na spacje. Yay: D Ekspansja Busuwa wszystko aż do pierwszego miejsca ze strumienia wejściowego i zastępuje przez HI#1FC#1|BHz #1bycia hello: HIhelloFChello|BHworld Q␣. Zauważ, że BHw strumieniu wejściowym pojawiło się nowe , aby wykonać rekursję ogonaBi przetwarzaj późniejsze słowa. Po przetworzeniu tego słowa przetwarza Bnastępne słowo, dopóki słowem do przetworzenia nie będzie kwark Q. QPotrzebna jest ostatnia spacja po , ponieważ rozdzielane makro B wymaga jednego na końcu argumentu. W poprzedniej wersji (patrz historia edycji) kod byłby źle zachowany, gdyby go używałeś \S{hello world}abc abc(odstęp między abcliterami zniknąłby).
OK, z powrotem do strumienia wejściowego: HIhelloFChello|BHworld Q␣. Najpierw jest H( \fi), który kończy inicjał \iftrue. Teraz mamy to (pseudokodowane):
I
hello
F
Chello|B
H
world Q␣
I...F...HZdaniem jest rzeczywiście \ifx Q...\else...\fistruktura. Na \ifxtest sprawdza, czy (pierwszy znak w słowie) chwycił jest Qtwaróg. Jeśli to nie ma nic innego do roboty, a kończy wykonanie, w przeciwnym razie, co pozostaje, to: Chello|BHworld Q␣. Teraz Cjest rozwinięty:
ACC#1#2|{D#2Q|#1 }
Pierwszy argument Cjest undelimited, więc jeśli usztywnione będzie pojedynczy element drugi argument jest ograniczona |, więc po rozszerzeniu C(z #1=ha #2=ello) strumień wejściowy: DelloQ|h BHworld Q␣. Zauważ, że inny |jest tam umieścić, a hod hellokładzie się po tym. Połowa zamiany jest zakończona; pierwsza litera jest na końcu. W TeX łatwo jest zdobyć pierwszy token z listy tokenów. Proste makro \def\first#1#2|{#1}otrzymuje pierwszą literę, gdy używasz \first hello|. Ten ostatni stanowi problem, ponieważ TeX zawsze pobiera jako argument argument „najmniejszą, być może pustą” listę tokenów, dlatego potrzebujemy kilku obejść. Kolejnym elementem na liście tokenów jest D:
ADD#1#2|{I#1FE{}#1#2|H}
To Dmakro jest jednym z obejść i jest przydatne w jedynym przypadku, gdy słowo ma jedną literę. Załóżmy, że zamiast tego hellomieliśmy x. W takim przypadku strumień wejściowy byłby DQ|xwtedyD rozszerzy (z #1=Qi #2opróżniania) i: IQFE{}Q|Hx. Jest to podobne do bloku I...F...H( \ifx Q...\else...\fi) w B, który zobaczy, że argumentem jest kwark i przerwie wykonywanie pozostawiając tylko xdo składu. W innych przypadkach (powracających na helloprzykład), Dby poszerzyć (z #1=ea #2=lloQ) na adres: IeFE{}elloQ|Hh BHworld Q␣. Ponownie, I...F...Hbędzie sprawdzał Q, ale nie powiedzie się i podjąć \elseoddział: E{}elloQ|Hh BHworld Q␣. Teraz ostatni kawałek tej rzeczy,E makro rozwinąłoby się:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
Tekst parametru tutaj jest dość podobny do Ci D; pierwszy i drugi argument są nieograniczone, a ostatni jest ograniczony przez |. Wygląda jak ten strumień wejściowy: E{}elloQ|Hh BHworld Q␣, a następnie Erozszerza się (z #1pusta #2=e, i #3=lloQ): IlloQeFE{e}lloQ|HHh BHworld Q␣. Kolejny I...F...Hblok sprawdza kwark (który widzi li zwraca false):E{e}lloQ|HHh BHworld Q␣ . Teraz Eponownie się rozszerza (z #1=epuste, #2=li #3=loQ) IloQleFE{el}loQ|HHHh BHworld Q␣. I znowu I...F...H. Makro wykonuje jeszcze kilka iteracji, aż w Qkońcu zostanie znalezione i truebrana jest gałąź: E{el}loQ|HHHh BHworld Q␣-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣-> E{ell}oQ|HHHHh BHworld Q␣-> IQoellFE{ello}Q|HHHHHh BHworld Q␣. Teraz kwark został odnaleziony i warunkowe rozszerza się do: oellHHHHh BHworld Q␣. Uff
Och, czekaj, co to jest? NORMALNE LITERY? O chłopie! Litery są w końcu znaleźć i TeX spisuje oell, to banda H( \fi) znajdują i rozszerzony (do zera), pozostawiając strumień wejściowy z: oellh BHworld Q␣. Teraz pierwsze słowo zamienia pierwszą i ostatnią literę, a TeX znajduje następne, Baby powtórzyć cały proces dla następnego słowa.
}
W końcu kończymy grupę, która zaczęła się tam ponownie, aby wszystkie zadania lokalne zostały cofnięte. Lokalne zadania są zmiany kotokodu liter A, B, C, ..., które zostały wykonane makra tak, że wracają do normalnego literę znaczenia i może być bezpiecznie stosowany w tekście. I to wszystko. Teraz \Szdefiniowane tam makro uruchomi przetwarzanie tekstu jak wyżej.
Jedną interesującą rzeczą w tym kodzie jest to, że można go w pełni rozbudowywać. Oznacza to, że możesz bezpiecznie używać go do przenoszenia argumentów bez obawy, że wybuchnie. Możesz nawet użyć kodu, aby sprawdzić, czy ostatnia litera słowa jest taka sama jak druga (z dowolnego powodu, którego byś potrzebował) w \ifteście:
\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
Przepraszam za (prawdopodobnie zdecydowanie zbyt) mylące wyjaśnienie. Starałem się, aby było to tak jasne, jak to możliwe, również dla TeXies :)
Podsumowanie dla niecierpliwych
Makro \Suzupełnia dane wejściowe aktywną postacią, Bktóra chwyta listy tokenów rozdzielonych spacją i przekazuje je do C. Cbierze pierwszy token z tej listy i przenosi go na koniec listy tokenów i rozwija Dwraz z resztą. Dsprawdza, czy „to, co pozostaje” jest puste, w którym to przypadku znaleziono jedno literowe słowo, a następnie nic nie rób; w przeciwnym razie rozszerza się E. Ezapętla listę tokenów, aż znajdzie ostatnią literę w słowie, po znalezieniu pozostawia ostatnią literę, a następnie środek słowa, a następnie pierwszą literę pozostawioną na końcu strumienia tokenu przez C.
Hello, world!staje się,elloH !orldw(zamiana interpunkcji na literę) luboellH, dorlw!(utrzymanie interpunkcji na miejscu)?