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 ( #1
jest ograniczony przez ␣to␣
spacje i #2
jest 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 Martin
wyda 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 \he11o
gdy sekwencja kontrolna, \he
po której następują dwa znaki 1
, a następnie litera o
, ponieważ 1
nie jest to kod kat. 11. Jeśli I tak \catcode`1=11
, od tego momentu \he11o
bę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! e
nie jest już listem, więc \def
nie \def
wiesz, 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ć R
w kodzie! To właśnie jest catcode 13. JESTEM SPOKOJNY! Przejdźmy dalej.
OK, teraz do grupowania. Jest to dość proste. Jakiekolwiek przypisania ( \def
jest 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} \a
to drukuje 1 2 1
. Na zewnątrz \a
był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 \let
operacja jest kolejną operacją przypisania \def
, ale raczej inną. Z \def
ty definiować makra, które rozwinie się rzeczy, o \let
utworzeniu kopii już istniejących rzeczy. Po \let\blub=\def
( =
opcja jest opcjonalna) możesz zmienić początek e
przykł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 R
przykł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=13
możemy napisać ~`A13
( =
jest opcjonalne):
~`A13
Teraz litera A
jest aktywną postacią i możemy ją zdefiniować, aby coś zrobić:
\defA#1{~`#113\gdef}
A
jest 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 \catcode
i dodaj =
i masz: \catcode`#1=13
). Wreszcie pozostawia \gdef
(globalny \def
) w strumieniu wejściowym. Krótko mówiąc, A
aktywuje inną postać i rozpoczyna jej definicję. Spróbujmy:
AGG#1{~`#113\global\let}
AG
pierwsza „aktywuje” G
i robi \gdef
, a następnie następna G
rozpoczyna definicję. Definicja G
jest bardzo podobna do tej A
, z tą różnicą, że zamiast \gdef
niej robi \global\let
(nie ma takiej \glet
jak \gdef
). Krótko mówiąc, G
aktywuje 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 \else
i \fi
możemy po prostu użyć F
i H
. Znacznie krótszy :)
AQQ{Q}
Teraz używamy A
ponownie 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, Q
jest Q
samo, więc działa jak unikalny znacznik (nazywa się kwark ). Możesz użyć \ifx
warunkowego 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 \ifx
i Q
. Zwykle prowadziłoby to do błędu (zwróć uwagę, że podświetlanie składni uważa, że \ifxQ
to jedno), ale ponieważ teraz Q
jest 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ż Q
rozwija się do Q
którego rozwija się do Q
któ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 E
definicja była tokenizowana jako litera (kod 11) przed zmianą kodu, więc zawsze będzie to litera E
. W drugim przykładzie E
został jednak najpierw uaktywniony, a dopiero potem \one
został zdefiniowany, a teraz definicja zawiera kod kat. 13, E
któ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 E
są 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 B
makro, po H
którym następuje (które wcześniej zdefiniowaliśmy \fi
) w celu dopasowania do \iftrue
. Następnie pozostawiamy argument makra, #1
po 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). \iftrue
jest 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 \fi
jest faktycznie rozszerzony. Teraz B
makro jest rozwinięte:
ABBH#1 {HI#1FC#1|BH}
B
jest ograniczonym makrem, którego tekstem parametru jest H#1␣
, więc argumentem jest cokolwiek pomiędzy H
spacją. Kontynuując przykład powyżej strumienia wejściowego przed rozszerzeniem B
jest BHhello world Q␣
. B
po nim następuje H
, tak jak powinien (w przeciwnym razie TeX spowodowałby błąd), następnie następna spacja znajduje się pomiędzy hello
i world
, podobnie #1
jak słowo hello
. I tutaj musimy podzielić tekst wejściowy na spacje. Yay: D Ekspansja B
usuwa wszystko aż do pierwszego miejsca ze strumienia wejściowego i zastępuje przez HI#1FC#1|BH
z #1
bycia hello
: HIhelloFChello|BHworld Q␣
. Zauważ, że BH
w strumieniu wejściowym pojawiło się nowe , aby wykonać rekursję ogonaB
i przetwarzaj późniejsze słowa. Po przetworzeniu tego słowa przetwarza B
następne słowo, dopóki słowem do przetworzenia nie będzie kwark Q
. Q
Potrzebna 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 abc
literami 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...H
Zdaniem jest rzeczywiście \ifx Q...\else...\fi
struktura. Na \ifx
test sprawdza, czy (pierwszy znak w słowie) chwycił jest Q
twaróg. Jeśli to nie ma nic innego do roboty, a kończy wykonanie, w przeciwnym razie, co pozostaje, to: Chello|BHworld Q␣
. Teraz C
jest rozwinięty:
ACC#1#2|{D#2Q|#1 }
Pierwszy argument C
jest undelimited, więc jeśli usztywnione będzie pojedynczy element drugi argument jest ograniczona |
, więc po rozszerzeniu C
(z #1=h
a #2=ello
) strumień wejściowy: DelloQ|h BHworld Q␣
. Zauważ, że inny |
jest tam umieścić, a h
od hello
kł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 D
makro jest jednym z obejść i jest przydatne w jedynym przypadku, gdy słowo ma jedną literę. Załóżmy, że zamiast tego hello
mieliśmy x
. W takim przypadku strumień wejściowy byłby DQ|x
wtedyD
rozszerzy (z #1=Q
i #2
opróż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 x
do składu. W innych przypadkach (powracających na hello
przykład), D
by poszerzyć (z #1=e
a #2=lloQ
) na adres: IeFE{}elloQ|Hh BHworld Q␣
. Ponownie, I...F...H
będzie sprawdzał Q
, ale nie powiedzie się i podjąć \else
oddział: 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 C
i 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 E
rozszerza się (z #1
pusta #2=e
, i #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Kolejny I...F...H
blok sprawdza kwark (który widzi l
i zwraca false
):E{e}lloQ|HHh BHworld Q␣
. Teraz E
ponownie się rozszerza (z #1=e
puste, #2=l
i #3=loQ
) IloQleFE{el}loQ|HHHh BHworld Q␣
. I znowu I...F...H
. Makro wykonuje jeszcze kilka iteracji, aż w Q
końcu zostanie znalezione i true
brana 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, B
aby 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 \S
zdefiniowane 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 \if
teś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 \S
uzupełnia dane wejściowe aktywną postacią, B
która chwyta listy tokenów rozdzielonych spacją i przekazuje je do C
. C
bierze pierwszy token z tej listy i przenosi go na koniec listy tokenów i rozwija D
wraz z resztą. D
sprawdza, 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
. E
zapę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)?