TeX, 216 octets (4 lignes, 54 caractères chacune)
Parce qu'il ne s'agit pas du nombre d'octets, il s'agit de la qualité de la sortie de composition :-)
{\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 }}
Essayez-le en ligne! (Au verso; je ne sais pas comment cela fonctionne)
Fichier de test complet:
{\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
Sortie:
Pour LaTeX, vous avez juste besoin du passe-partout:
\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}
Explication
TeX est une étrange bête. Lire du code normal et le comprendre est un exploit en soi. La compréhension du code TeX obscurci va plus loin. Je vais essayer de rendre cela compréhensible pour les personnes qui ne connaissent pas aussi TeX, donc avant de commencer, voici quelques concepts sur TeX pour rendre les choses plus faciles à suivre:
Pour (pas tellement) les débutants absolus de TeX
Tout d' abord, et élément le plus important dans cette liste: le code ne pas avoir à être en forme rectangulaire, même si la culture pop pourrait vous amener à penser .
TeX est un langage d'expansion de macro. Vous pouvez, par exemple, définir \def\sayhello#1{Hello, #1!}
puis écrire \sayhello{Code Golfists}
pour que TeX s'imprime Hello, Code Golfists!
. C'est ce qu'on appelle une «macro non délimitée», et pour lui donner le premier (et seulement, dans ce cas) paramètre, vous la mettez entre accolades. TeX supprime ces accolades lorsque la macro prend l'argument. Vous pouvez utiliser jusqu'à 9 paramètres: \def\say#1#2{#1, #2!}
alors \say{Good news}{everyone}
.
La contrepartie de macros sont non délimitées, sans surprise, les délimité :) Vous pouvez faire la définition précédente un peu plus sémantiquement : \def\say #1 to #2.{#1, #2!}
. Dans ce cas, les paramètres sont suivis d'un texte dit de paramètres . Un tel texte de paramètre délimite l'argument de la macro ( #1
est délimité par ␣to␣
, espaces inclus et #2
est délimité par .
). Après cette définition, vous pouvez écrire \say Good news to everyone.
, qui se développera Good news, everyone!
. Sympa, non? :) Cependant, un argument délimité est (en citant le TeXbook ) "la séquence de jetons la plus courte (éventuellement vide) avec des {...}
groupes correctement imbriqués qui est suivie en entrée par cette liste particulière de jetons non paramétriques". Cela signifie que l'expansion de\say Let's go to the mall to Martin
produira une phrase bizarre. Dans ce cas , vous auriez besoin de « cacher » la première ␣to␣
avec {...}
: \say {Let's go to the mall} to Martin
.
Jusqu'ici tout va bien. Maintenant, les choses commencent à devenir bizarres. Lorsque TeX lit un caractère (qui est défini par un «code de caractère»), il attribue à ce caractère un «code de catégorie» (catcode, pour les amis :) qui définit ce que ce caractère signifie. Cette combinaison de caractères et de code de catégorie fait un jeton (plus d'informations ici , par exemple). Ceux qui nous intéressent ici sont essentiellement:
catcode 11 , qui définit des jetons qui peuvent constituer une séquence de contrôle (un nom chic pour une macro). Par défaut, toutes les lettres [a-zA-Z] sont catcode 11, donc je peux écrire \hello
, qui est une seule séquence de contrôle, tandis que \he11o
la séquence de contrôle est \he
suivie de deux caractères 1
, suivie de la lettre o
, car ce 1
n'est pas le catcode 11. Si je a \catcode`1=11
, à partir de ce moment-là \he11o
serait une séquence de contrôle. Une chose importante est que les catcodes sont définis lorsque TeX voit pour la première fois le personnage à portée de main, et ce catcode est gelé ... POUR TOUJOURS ! (Les termes et conditions peuvent s'appliquer)
catcode 12 , qui sont la plupart des autres caractères, tels que 0"!@*(?,.-+/
et ainsi de suite. Ils sont le type de catcode le moins spécial car ils ne servent qu'à écrire des trucs sur le papier. Mais bon, qui utilise TeX pour écrire?!? (encore une fois, des conditions générales peuvent s'appliquer)
catcode 13 , qui est l'enfer :) Vraiment. Arrêtez de lire et allez faire quelque chose de votre vie. Vous ne voulez pas savoir ce qu'est le catcode 13. Avez-vous déjà entendu parler du vendredi 13? Devinez d'où il tire son nom! Continuez à vos risques et périls! Un caractère catcode 13, également appelé caractère «actif», n'est plus seulement un caractère, c'est une macro en soi! Vous pouvez le définir pour avoir des paramètres et développer quelque chose comme nous l'avons vu ci-dessus. Une fois que vous le faites , \catcode`e=13
vous pensez que vous pouvez faire \def e{I am the letter e!}
, MAIS. TOI. NE PEUX PAS! e
n'est plus une lettre, donc ce \def
n'est pas ce que \def
vous savez, ça l'est \d e f
! Oh, choisissez une autre lettre que vous dites? D'accord! \catcode`R=13 \def R{I am an ARRR!}
. Très bien, Jimmy, essaye! J'ose vous faire ça et écrire un R
dans votre code! C'est ce qu'est un catcode 13. JE SUIS CALME! Allons-nous en.
D'accord, maintenant au regroupement. C'est assez simple. Quelles que soient les affectations (il \def
s'agit d'une opération d'affectation \let
(nous y reviendrons) en est une autre) effectuées dans un groupe sont restaurées à ce qu'elles étaient avant le démarrage de ce groupe, sauf si cette affectation est globale. Il existe plusieurs façons de démarrer des groupes, l'un d'eux est avec les caractères catcode 1 et 2 (oh, catcodes à nouveau). Par défaut {
est catcode 1, ou begin-group, et }
est catcode 2, ou end-group. Un exemple: \def\a{1} \a{\def\a{2} \a} \a
cela s'imprime 1 2 1
. En dehors du groupe \a
était 1
, puis à l' intérieur , il a été redéfinie 2
, et quand le groupe a pris fin, il a été restauré 1
.
L' \let
opération est une autre opération d'affectation comme \def
, mais plutôt différente. Avec \def
vous définissez des macros qui se développeront en choses, avec \let
vous créez des copies de choses déjà existantes. Après \let\blub=\def
(le =
est facultatif), vous pouvez changer le début de l' e
exemple de l'élément catcode 13 ci-dessus pour \blub e{...
vous amuser avec celui-ci. Ou mieux, au lieu de casser des choses que vous pouvez fixer (chercheriez-vous à cela!) L' R
exemple: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
. Question rapide: pourriez-vous renommer \newR
?
Enfin, les soi-disant «espaces parasites». C'est une sorte de sujet tabou car il y a des gens qui prétendent que la réputation gagnée dans l' échange de piles TeX - LaTeX en répondant à des questions sur les «espaces parasites» ne devrait pas être prise en compte, tandis que d'autres sont en désaccord sans réserve. Avec qui êtes-vous d'accord? Placez vos paris! Pendant ce temps: TeX comprend un saut de ligne comme un espace. Essayez d'écrire plusieurs mots avec un saut de ligne (pas une ligne vide ) entre eux. Ajoutez maintenant un %
à la fin de ces lignes. C'est comme si vous «commentiez» ces espaces de fin de ligne. C'est ça :)
(Sorte de) dé-golfer le code
Faisons de ce rectangle quelque chose (sans doute) plus facile à suivre:
{
\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 }
}
Explication de chaque étape
chaque ligne contient une seule instruction. Allons un par un, les disséquant:
{
Tout d'abord, nous commençons un groupe pour garder certaines modifications (à savoir les modifications de code de cat) locales afin qu'elles ne gâchent pas le texte d'entrée.
\let~\catcode
Fondamentalement, tous les codes d'obscurcissement TeX commencent par cette instruction. Par défaut, à la fois dans TeX simple et LaTeX, le ~
caractère est le seul caractère actif qui peut être transformé en macro pour une utilisation ultérieure. Et le meilleur outil pour étrangler le code TeX sont les changements de catcode, c'est donc généralement le meilleur choix. Maintenant, au lieu de \catcode`A=13
nous pouvons écrire ~`A13
(le =
est facultatif):
~`A13
Maintenant, la lettre A
est un caractère actif, et nous pouvons le définir pour faire quelque chose:
\defA#1{~`#113\gdef}
A
est maintenant une macro qui prend un argument (qui devrait être un autre caractère). Tout d'abord, le code cat de l'argument est changé en 13 pour le rendre actif: ~`#113
(remplacez le ~
par \catcode
et ajoutez un =
et vous avez :) \catcode`#1=13
. Enfin, il laisse un \gdef
(global \def
) dans le flux d'entrée. En bref, A
rend un autre personnage actif et commence sa définition. Essayons:
AGG#1{~`#113\global\let}
AG
le premier «s'active» G
et le fait \gdef
, ce qui, suivi du suivant, G
démarre la définition. La définition de G
est très similaire à celle de A
, sauf qu'au lieu de \gdef
cela, il y a un \global\let
(il n'y en a pas \glet
comme le \gdef
). En bref, G
active un personnage et en fait quelque chose d'autre. Faisons des raccourcis pour deux commandes que nous utiliserons plus tard:
GFF\else
GHH\fi
Maintenant, au lieu de \else
et \fi
nous pouvons simplement utiliser F
et H
. Plus court :)
AQQ{Q}
Maintenant , nous utilisons à A
nouveau pour définir une autre macro, Q
. La déclaration ci-dessus fait essentiellement (dans un langage moins obscur) \def\Q{\Q}
. Ce n'est pas une définition terriblement intéressante, mais elle a une fonctionnalité intéressante. À moins que vous ne vouliez casser du code, la seule macro qui se développe Q
est Q
elle - même, elle agit donc comme un marqueur unique (on l'appelle un quark ). Vous pouvez utiliser le \ifx
conditionnel pour tester si l'argument d'une macro est tel quark avec \ifx Q#1
:
AII{\ifxQ}
vous pouvez donc être sûr que vous avez trouvé un tel marqueur. Notez que dans cette définition, j'ai supprimé l'espace entre \ifx
et Q
. Habituellement, cela entraînerait une erreur (notez que la mise en évidence de la syntaxe pense que \ifxQ
c'est une chose), mais comme il Q
s'agit maintenant de catcode 13, il ne peut pas former une séquence de contrôle. Attention, cependant, à ne pas étendre ce quark ou vous serez coincé dans une boucle infinie car se Q
dilate vers Q
lequel se développe vers Q
lequel ...
Maintenant que les préliminaires sont terminés, nous pouvons passer à l'algorithme approprié pour pwas eht setterl. En raison de la tokenisation de TeX, l'algorithme doit être écrit à l'envers. En effet, au moment où vous faites une définition, TeX va tokenize (attribuer des catcodes) aux caractères de la définition en utilisant les paramètres actuels, par exemple, si je le fais:
\def\one{E}
\catcode`E=13\def E{1}
\one E
la sortie est E1
, alors que si je change l'ordre des définitions:
\catcode`E=13\def E{1}
\def\one{E}
\one E
la sortie est 11
. En effet, dans le premier exemple, la E
définition a été symbolisée sous forme de lettre (catcode 11) avant le changement de catcode, ce sera donc toujours une lettre E
. Dans le deuxième exemple, cependant, a E
été rendu actif pour la première fois, et seulement alors a \one
été défini, et maintenant la définition contient le code cat 13 E
qui se développe 1
.
Je vais cependant ignorer ce fait et réorganiser les définitions pour avoir un ordre logique (mais pas fonctionnel). Dans les paragraphes qui suivent , vous pouvez supposer que les lettres B
, C
, D
et E
sont actifs.
\gdef\S#1{\iftrueBH#1 Q }
(remarquez qu'il y avait un petit bug dans la version précédente, il ne contenait pas l'espace final dans la définition ci-dessus. Je l'ai seulement remarqué en écrivant ceci. Lisez la suite et vous verrez pourquoi nous avons besoin de celui-ci pour terminer correctement la macro. ) Tout d'
abord , nous définissons la macro-niveau utilisateur, \S
. Celui-ci ne doit pas être un caractère actif pour avoir une syntaxe conviviale (?), Donc la macro pour gwappins eht setterl est \S
. La macro commence par un conditionnel toujours vrai \iftrue
(il sera bientôt clair pourquoi), puis appelle la B
macro suivie de H
(que nous avons définie précédemment comme étant \fi
) pour correspondre à \iftrue
. Ensuite, nous laissons l'argument de la macro #1
suivi d'un espace et du quark Q
. Supposons que nous utilisons \S{hello world}
, alors le flux d'entréedevrait ressembler à ceci:\iftrue BHhello world Q␣
(J'ai remplacé le dernier espace par un ␣
pour que le rendu du site ne le mange pas, comme je le faisais dans la version précédente du code). \iftrue
est vrai, donc il se développe et nous nous retrouvons avec BHhello world Q␣
. TeX ne supprime pas le \fi
( H
) après l'évaluation du conditionnel, mais le laisse là jusqu'à ce que le \fi
soit réellement développé. Maintenant, la B
macro est développée:
ABBH#1 {HI#1FC#1|BH}
B
est une macro délimitée dont le texte du paramètre est H#1␣
, donc l'argument est tout ce qui est entre H
et un espace. Poursuivant l'exemple au-dessus du flux d'entrée avant l'expansion de B
is BHhello world Q␣
. B
est suivi H
, comme il se doit (sinon TeX soulèverait une erreur), alors l'espace suivant est entre hello
et world
, #1
le mot aussi hello
. Et ici, nous devons diviser le texte d'entrée dans les espaces. Yay: D L'expansion de B
Enlève tout jusqu'au premier espace à partir du flux d'entrée et remplace par HI#1FC#1|BH
d' #1
être hello
: HIhelloFChello|BHworld Q␣
. Notez qu'il y a un nouveau BH
plus tard dans le flux d'entrée, pour faire une récursion de queue deB
et traiter les mots ultérieurs. Une fois ce mot traité, il B
traite le mot suivant jusqu'à ce que le mot à traiter soit le quark Q
. Le dernier espace après Q
est nécessaire car la macro délimitée en B
requiert un à la fin de l'argument. Avec la version précédente (voir l'historique des modifications), le code se comporterait mal si vous l'utilisiez \S{hello world}abc abc
(l'espace entre les abc
s disparaîtrait).
OK, retour au flux d'entrée: HIhelloFChello|BHworld Q␣
. Il y a d'abord le H
( \fi
) qui complète l'initiale \iftrue
. Maintenant, nous avons ceci (pseudocodé):
I
hello
F
Chello|B
H
world Q␣
La I...F...H
réflexion est en fait une \ifx Q...\else...\fi
structure. Le \ifx
test vérifie si le (premier jeton du) mot saisi est le Q
quark. S'il est il n'y a rien d' autre à faire et les termine d'exécution, sinon ce qui reste est: Chello|BHworld Q␣
. Maintenant C
est développé:
ACC#1#2|{D#2Q|#1 }
Le premier argument de C
est non délimité, de sorte que si contreventés ce sera un jeton unique, le second argument est délimitée par |
, donc après l'expansion de la C
(avec #1=h
et #2=ello
) le flux d'entrée est la suivante : DelloQ|h BHworld Q␣
. Remarquez qu'un autre |
est mis là, et le h
of hello
est mis après. La moitié de l'échange est effectuée; la première lettre est à la fin. Dans TeX, il est facile de récupérer le premier jeton d'une liste de jetons. Une macro simple \def\first#1#2|{#1}
obtient la première lettre lorsque vous utilisez \first hello|
. Le dernier est un problème car TeX saisit toujours la liste de jetons "la plus petite, éventuellement vide" comme argument, nous avons donc besoin de quelques solutions. L'élément suivant dans la liste des jetons est D
:
ADD#1#2|{I#1FE{}#1#2|H}
Cette D
macro est l'une des solutions de contournement et elle est utile dans le seul cas où le mot a une seule lettre. Supposons qu'au lieu de hello
nous x
. Dans ce cas , le flux d'entrée serait DQ|x
alors D
élargirait (avec #1=Q
et #2
vide) à: IQFE{}Q|Hx
. Ceci est similaire au bloc I...F...H
( \ifx Q...\else...\fi
) dans B
, qui verra que l'argument est le quark et interrompra l'exécution en ne laissant que la x
composition. Dans d' autres cas (retour à l' hello
exemple), D
élargirait (avec #1=e
et #2=lloQ
) à: IeFE{}elloQ|Hh BHworld Q␣
. Encore une fois, le I...F...H
vérifiera Q
mais échouera et prendre la \else
branche: E{}elloQ|Hh BHworld Q␣
. Maintenant, le dernier morceau de cette chose, leE
la macro se développerait:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
Le texte du paramètre ici est assez similaire à C
et D
; les premier et deuxième arguments ne sont pas délimités et le dernier est délimité par |
. Le flux d'entrée ressemble à ceci: E{}elloQ|Hh BHworld Q␣
, puis se E
dilate (avec #1
vide, #2=e
, et #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Un autre I...F...H
bloc vérifie pour le quark (qui voit l
et retourne false
): E{e}lloQ|HHh BHworld Q␣
. Maintenant , se E
développe à nouveau (avec #1=e
vide, #2=l
et #3=loQ
): IloQleFE{el}loQ|HHHh BHworld Q␣
. Et encore I...F...H
. La macro effectue quelques itérations supplémentaires jusqu'à ce que le Q
soit finalement trouvé et que la true
branche soit prise: E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> IQoellFE{ello}Q|HHHHHh BHworld Q␣
. Maintenant , le quark se trouve et les conditionnels à se dilate: oellHHHHh BHworld Q␣
. Phew.
Oh, attendez, qu'est-ce que c'est? LETTRES NORMALES? Oh mec! Les lettres sont finalement trouvés et TeX écrit vers le bas oell
, puis un tas de H
( \fi
) se trouvent et étendu (rien) en laissant le flux d'entrée avec: oellh BHworld Q␣
. Maintenant, le premier mot a la première et la dernière lettres échangées et ce que TeX trouve ensuite est l'autre B
pour répéter tout le processus pour le mot suivant.
}
Enfin, nous terminons le groupe commencé là-bas afin que toutes les affectations locales soient annulées. Les missions locales sont les changements de catcode des lettres A
, B
, C
... qui ont été faites des macros pour qu'ils reviennent à leur signification normale de lettre et peuvent être utilisés en toute sécurité dans le texte. Et c'est tout. Maintenant, la \S
macro définie là-bas déclenchera le traitement du texte comme ci-dessus.
Une chose intéressante à propos de ce code est qu'il est entièrement extensible. Autrement dit, vous pouvez l'utiliser en toute sécurité pour déplacer des arguments sans craindre qu'il explose. Vous pouvez même utiliser le code pour vérifier si la dernière lettre d'un mot est la même que la seconde (pour une raison quelconque, vous en aurez besoin) dans un \if
test:
\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
Désolé pour l'explication (probablement beaucoup trop) verbeuse. J'ai essayé de le rendre aussi clair que possible pour les non TeXies :)
Résumé pour les impatients
La macro \S
ajoute à l'entrée un caractère actif B
qui récupère les listes de jetons délimités par un espace final et les transmet à C
. C
prend le premier jeton de cette liste et le déplace à la fin de la liste de jetons et se développe D
avec ce qui reste. D
vérifie si «ce qui reste» est vide, auquel cas un mot d'une seule lettre a été trouvé, puis ne fait rien; sinon se développe E
. E
parcourt la liste des jetons jusqu'à ce qu'il trouve la dernière lettre du mot, lorsqu'il le trouve, il laisse cette dernière lettre, suivi du milieu du mot, qui est ensuite suivi de la première lettre laissée à la fin du flux de jetons par C
.
Hello, world!
devient,elloH !orldw
(permutation de la ponctuation en lettre) ouoellH, dorlw!
(maintien de la ponctuation en place)?