TeX, 216 byte (4 righe, 54 caratteri ciascuno)
Poiché non si tratta del conteggio dei byte, si tratta della qualità dell'output composto :-)
{\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 }}
Provalo online! (Sul retro; non sono sicuro di come funzioni)
File di test completo:
{\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
Produzione:
Per LaTeX hai solo bisogno della piastra della caldaia:
\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}
Spiegazione
TeX è una strana bestia. Leggere il codice normale e comprenderlo è di per sé un'impresa. La comprensione del codice TeX offuscato va oltre. Cercherò di renderlo comprensibile anche per le persone che non conoscono TeX, quindi prima di iniziare ecco alcuni concetti su TeX per rendere le cose più facili da seguire:
Per (non così) principianti assoluti TeX
In primo luogo, e l'elemento più importante in questo elenco: il codice non deve essere in forma rettangolare, anche se la cultura pop potrebbe indurti a pensarlo .
TeX è un linguaggio di espansione macro. È possibile, ad esempio, definire \def\sayhello#1{Hello, #1!}
e quindi scrivere \sayhello{Code Golfists}
per ottenere la stampa di TeX Hello, Code Golfists!
. Questa è chiamata "macro non delimitata" e per alimentare il primo (e unico, in questo caso) parametro lo racchiudi tra parentesi graffe. TeX rimuove quelle parentesi graffe quando la macro afferra l'argomento. È possibile utilizzare fino a 9 parametri: \def\say#1#2{#1, #2!}
quindi \say{Good news}{everyone}
.
La controparte di macro undelimited sono, ovviamente, quelli delimitati :) Si potrebbe fare la precedente definizione un po 'più semantico : \def\say #1 to #2.{#1, #2!}
. In questo caso i parametri sono seguiti dal cosiddetto testo del parametro . Tale testo di parametro delimita l'argomento della macro ( #1
è delimitato da ␣to␣
, spazi inclusi ed #2
è delimitato da .
). Dopo quella definizione puoi scrivere \say Good news to everyone.
, che si espanderà a Good news, everyone!
. Bello no? :) Tuttavia, un argomento delimitato è (citando il TeXbook ) "la sequenza più breve (possibilmente vuota) di token con {...}
gruppi nidificati correttamente che è seguita nell'input da questo particolare elenco di token non parametrici". Ciò significa che l'espansione di\say Let's go to the mall to Martin
produrrà una strana frase. In questo caso si avrebbe bisogno di “nascondere” il primo ␣to␣
con {...}
: \say {Let's go to the mall} to Martin
.
Fin qui tutto bene. Ora le cose iniziano a diventare strane. Quando TeX legge un carattere (che è definito da un "codice carattere"), assegna a quel carattere un "codice categoria" (codice cat, per gli amici :) che definisce cosa significherà quel carattere. Questa combinazione di carattere e codice categoria crea un token (ne parleremo qui , per esempio). Quelli che ci interessano qui sono fondamentalmente:
catcode 11 , che definisce i token che possono costituire una sequenza di controllo (un nome elegante per una macro). Per impostazione predefinita, tutte le lettere [a-zA-Z] sono catcode 11, quindi posso scrivere \hello
, che è una singola sequenza di controllo, mentre \he11o
la sequenza di controllo è \he
seguita da due caratteri 1
, seguita dalla lettera o
, perché 1
non è il catcode 11. Se I fatto \catcode`1=11
, da quel momento in poi \he11o
sarebbe una sequenza di controllo. Una cosa importante è che i codici cat vengono impostati quando TeX vede per la prima volta il personaggio a portata di mano e tale codice cat viene bloccato ... PER SEMPRE! (possono essere applicati termini e condizioni)
catcode 12 , che sono la maggior parte degli altri caratteri, come 0"!@*(?,.-+/
e così via. Sono il tipo meno speciale di catcode in quanto servono solo per scrivere cose sulla carta. Ma hey, chi usa TeX per scrivere?!? (di nuovo, possono essere applicati termini e condizioni)
catcode 13 , che è un inferno :) Davvero. Smetti di leggere e vai a fare qualcosa fuori dalla tua vita. Non vuoi sapere cos'è il catcode 13. Hai mai sentito parlare di venerdì 13? Indovina da dove prende il nome! Continua a tuo rischio e pericolo! Un carattere catcode 13, chiamato anche carattere "attivo", non è più solo un personaggio, è una macro stessa! Puoi definirlo per avere parametri ed espandersi a qualcosa come abbiamo visto sopra. Dopo aver fatto \catcode`e=13
si pensa che si può fare \def e{I am the letter e!}
, MA. TU. NON PUÒ! e
non è più una lettera, quindi \def
non lo \def
sai, lo è \d e f
! Oh, scegli un'altra lettera che dici? Va bene! \catcode`R=13 \def R{I am an ARRR!}
. Molto bene, Jimmy, provalo! Ti sfido a farlo e scrivere un R
nel tuo codice! Ecco cos'è un catcode 13. SONO CALMO! Andiamo avanti.
Bene, ora al raggruppamento. Questo è abbastanza semplice. Qualunque incarico ( \def
è un'operazione di incarico, \let
(entreremo in esso) è un altro) fatto in un gruppo viene ripristinato a quello che era prima dell'inizio di quel gruppo a meno che quell'assegnazione non fosse globale. Esistono diversi modi per avviare gruppi, uno di questi è con catcode 1 e 2 caratteri (oh, di nuovo catcode). Per impostazione predefinita {
è catcode 1, o gruppo iniziale, ed }
è catcode 2 o gruppo finale. Un esempio: \def\a{1} \a{\def\a{2} \a} \a
questo stampa 1 2 1
. All'esterno il gruppo è \a
stato 1
, quindi all'interno è stato ridefinito 2
e, quando il gruppo è terminato, è stato ripristinato 1
.
L' \let
operazione è un'altra operazione di assegnazione simile \def
, ma piuttosto diversa. Con \def
te definisci le macro che si espanderanno in cose, con \let
te crei copie di cose già esistenti. Dopo \let\blub=\def
( =
è facoltativo) puoi cambiare l'inizio e
dell'esempio dall'elemento catcode 13 sopra a \blub e{...
e divertirti con quello. O meglio, invece di rompere roba che si può risolvere (vuoi guardare quella!) L' R
esempio \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
. Domanda veloce: potresti rinominare \newR
?
Infine, i cosiddetti "spazi spuri". Questo è un tipo di argomento tabù perché ci sono persone che affermano che la reputazione guadagnata nel TeX - LaTeX Stack Exchange rispondendo a domande su "spazi spuri" non dovrebbe essere presa in considerazione, mentre altri non sono assolutamente d'accordo. Con chi sei d'accordo? Fate le vostre puntate! Nel frattempo: TeX comprende un'interruzione di linea come spazio. Prova a scrivere più parole con un'interruzione di riga (non una riga vuota ) tra loro. Ora aggiungi %
a alla fine di queste righe. È come se stessi “commentando” questi spazi di fine linea. Questo è tutto :)
(Sorta di) annullamento della memorizzazione del codice
Trasformiamo quel rettangolo in qualcosa (probabilmente) più facile da seguire:
{
\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 }
}
Spiegazione di ogni passaggio
ogni riga contiene una singola istruzione. Andiamo uno per uno, dissezionandoli:
{
Innanzitutto iniziamo un gruppo per mantenere locali alcuni cambiamenti (vale a dire i cambiamenti di catcode) in modo che non rovinino il testo di input.
\let~\catcode
Fondamentalmente tutti i codici di offuscamento TeX iniziano con questa istruzione. Per impostazione predefinita, sia in TeX che in LaTeX, il ~
personaggio è l'unico personaggio attivo che può essere trasformato in una macro per un ulteriore utilizzo. E lo strumento migliore per personalizzare il codice TeX sono le modifiche al codice cat, quindi questa è generalmente la scelta migliore. Ora invece di \catcode`A=13
scrivere ~`A13
( =
è facoltativo):
~`A13
Ora la lettera A
è un personaggio attivo e possiamo definirla per fare qualcosa:
\defA#1{~`#113\gdef}
A
è ora una macro che accetta un argomento (che dovrebbe essere un altro carattere). Per prima cosa il catcode dell'argomento viene modificato in 13 per renderlo attivo: ~`#113
(sostituisci ~
da \catcode
e aggiungi un =
e hai :) \catcode`#1=13
. Infine lascia un \gdef
(globale \def
) nel flusso di input. In breve, A
rende attivo un altro personaggio e inizia la sua definizione. Proviamolo:
AGG#1{~`#113\global\let}
AG
prima "attiva" G
e fa \gdef
, che seguito dalla successiva G
inizia la definizione. La definizione di G
è molto simile a quella di A
, tranne che invece di \gdef
un \global\let
(non c'è un \glet
simile al \gdef
). In breve, G
attiva un personaggio e lo rende qualcos'altro. Facciamo scorciatoie per due comandi che useremo in seguito:
GFF\else
GHH\fi
Ora invece di \else
e \fi
possiamo semplicemente usare F
e H
. Molto più breve :)
AQQ{Q}
Ora usiamo A
di nuovo per definire un'altra macro, Q
. L'affermazione di cui sopra sostanzialmente funziona (in una lingua meno offuscata) \def\Q{\Q}
. Questa non è una definizione terribilmente interessante, ma ha una caratteristica interessante. A meno che tu non voglia rompere un po 'di codice, l'unica macro che si espande Q
è Q
se stessa, quindi si comporta come un marcatore univoco (si chiama quark ). È possibile utilizzare il \ifx
condizionale per verificare se l'argomento di una macro è tale quark con \ifx Q#1
:
AII{\ifxQ}
quindi puoi essere abbastanza sicuro di aver trovato un tale marker. Si noti che in questa definizione ho rimosso lo spazio tra \ifx
e Q
. Di solito questo porta a un errore (notare che l'evidenziazione della sintassi pensa che \ifxQ
sia una cosa), ma poiché ora Q
è il catcode 13 non può formare una sequenza di controllo. Fai attenzione, tuttavia, a non espandere questo quark o rimarrai bloccato in un ciclo infinito perché si Q
espande a Q
quale si espande a Q
quale ...
Ora che i preliminari sono terminati, possiamo passare all'algoritmo corretto per essere pronti. A causa della tokenizzazione di TeX, l'algoritmo deve essere scritto al contrario. Questo perché al momento in cui si effettua una definizione TeX tokenizzerà (assegnando i codici cat) ai caratteri nella definizione utilizzando le impostazioni correnti, quindi, ad esempio, se lo faccio:
\def\one{E}
\catcode`E=13\def E{1}
\one E
l'output è E1
, mentre se cambio l'ordine delle definizioni:
\catcode`E=13\def E{1}
\def\one{E}
\one E
l'uscita è 11
. Questo perché nel primo esempio E
nella definizione è stato tokenizzato come una lettera (catcode 11) prima della modifica del catcode, quindi sarà sempre una lettera E
. Nel secondo esempio, tuttavia, è E
stato prima reso attivo e solo allora è \one
stato definito, e ora la definizione contiene il catcode 13 E
che si espande a 1
.
Tuttavia, trascurerò questo fatto e riordinerò le definizioni per avere un ordine logico (ma non funzionante). Nei paragrafi che seguono si può presumere che le lettere B
, C
, D
e E
sono attivi.
\gdef\S#1{\iftrueBH#1 Q }
(nota che c'era un piccolo bug nella versione precedente, non conteneva lo spazio finale nella definizione sopra. L'ho notato solo mentre scrivevo questo. Continua a leggere e vedrai perché ne abbiamo bisogno per terminare correttamente la macro. )
Per prima cosa definire la macro-livello utente, \S
. Questo non dovrebbe essere un personaggio attivo per avere una sintassi amichevole (?), Quindi la macro per gwappins eht setterl è \S
. La macro inizia con un condizionale sempre vero \iftrue
(sarà presto chiaro il perché), quindi chiama la B
macro seguita da H
(che abbiamo definito in precedenza \fi
) per corrispondere a \iftrue
. Quindi lasciamo l'argomento della macro #1
seguito da uno spazio e dal quark Q
. Supponiamo di usare \S{hello world}
, quindi il flusso di inputdovrebbe apparire così: \iftrue BHhello world Q␣
(ho sostituito l'ultimo spazio con un in ␣
modo che il rendering del sito non lo mangi, come ho fatto nella versione precedente del codice). \iftrue
è vero, quindi si espande e ci rimane BHhello world Q␣
. TeX non rimuove il \fi
( H
) dopo aver valutato il condizionale, invece lo lascia lì fino a quando non \fi
viene effettivamente espanso. Ora la B
macro è espansa:
ABBH#1 {HI#1FC#1|BH}
B
è una macro delimitata il cui parametro è il testo H#1␣
, quindi l'argomento è qualunque sia tra H
e uno spazio. Continuando l'esempio sopra il flusso di input prima dell'espansione di B
is BHhello world Q␣
. B
è seguito da H
, come dovrebbe (altrimenti TeX genererebbe un errore), quindi lo spazio successivo è tra hello
e world
, così #1
è la parola hello
. E qui dobbiamo dividere il testo di input negli spazi. Yay: D L'espansione B
rimuove tutto fino al primo spazio dal flusso di input e sostituisce da HI#1FC#1|BH
con #1
essere hello
: HIhelloFChello|BHworld Q␣
. Si noti che è disponibile una nuova versione BH
successiva nel flusso di input, per eseguire una ricorsione della codaB
ed elaborare le parole successive. Dopo che questa parola è stata elaborata B
elabora la parola successiva fino a quando la parola da elaborare è il quark Q
. L'ultimo spazio dopo Q
è necessario perché la macro delimitata B
richiede uno alla fine dell'argomento. Con la versione precedente (vedi cronologia delle modifiche) il codice si comporterebbe in modo errato se lo utilizzassi \S{hello world}abc abc
(lo spazio tra le abc
s svanirebbe).
OK, torniamo al flusso di input: HIhelloFChello|BHworld Q␣
. Innanzitutto c'è il H
( \fi
) che completa l'iniziale \iftrue
. Ora abbiamo questo (pseudocodificato):
I
hello
F
Chello|B
H
world Q␣
Il I...F...H
pensiero è in realtà una \ifx Q...\else...\fi
struttura. Il \ifx
test verifica se la parola (primo token della) catturata è il Q
quark. Se è non c'è niente altro da fare e le termina l'esecuzione, altrimenti ciò che rimane è: Chello|BHworld Q␣
. Ora C
è espanso:
ACC#1#2|{D#2Q|#1 }
Il primo argomento della C
è undelimited, quindi a meno rinforzato sarà un unico token, il secondo argomento è delimitato da |
, così dopo l'espansione C
(con #1=h
e #2=ello
) il flusso di ingresso è: DelloQ|h BHworld Q␣
. Si noti che un altro |
è messo lì, e il h
di hello
è messo dopo. La metà dello scambio è fatta; la prima lettera è alla fine. In TeX è facile afferrare il primo token di un elenco di token. Una semplice macro \def\first#1#2|{#1}
ottiene la prima lettera quando si utilizza \first hello|
. L'ultimo è un problema perché TeX afferra sempre la lista di token “più piccola, possibilmente vuota” come argomento, quindi abbiamo bisogno di qualche soluzione. L'elemento successivo nell'elenco token è D
:
ADD#1#2|{I#1FE{}#1#2|H}
Questa D
macro è una delle soluzioni alternative ed è utile nel solo caso in cui la parola ha una sola lettera. Supponiamo invece di hello
averlo fatto x
. In questo caso il flusso di ingresso sarebbe DQ|x
, allora D
dovrebbe espandere (con #1=Q
e #2
svuotare) a: IQFE{}Q|Hx
. Questo è simile al blocco I...F...H
( \ifx Q...\else...\fi
) in B
, che vedrà che l'argomento è il quark e interromperà l'esecuzione lasciando solo x
per la composizione. In altri casi (tornando alla hello
esempio), D
dovrebbe espandere (con #1=e
e #2=lloQ
) a: IeFE{}elloQ|Hh BHworld Q␣
. Anche in questo caso, la I...F...H
verifica la presenza di Q
, ma non riuscirà e prendere la \else
filiale: E{}elloQ|Hh BHworld Q␣
. Ora l'ultimo pezzo di questa cosa, ilE
la macro si espanderebbe:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
Il testo del parametro qui è abbastanza simile a C
e D
; il primo e il secondo argomento non sono delimitati e l'ultimo è delimitato da |
. L'aspetto del flusso di ingresso di questo tipo: E{}elloQ|Hh BHworld Q␣
, poi E
si espande (con #1
vuoto, #2=e
, e #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Un'altra I...F...H
verifica di blocco del quark (che vede l
e ritorna false
): E{e}lloQ|HHh BHworld Q␣
. Ora E
si espande nuovamente (con #1=e
vuoto, #2=l
e #3=loQ
): IloQleFE{el}loQ|HHHh BHworld Q␣
. E ancora I...F...H
. La macro esegue alcune altre iterazioni fino a quando non Q
viene finalmente trovata e il true
ramo viene preso: E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> IQoellFE{ello}Q|HHHHHh BHworld Q␣
. Ora il quark viene trovato e si espande condizionali a: oellHHHHh BHworld Q␣
. Uff.
Oh, aspetta, cosa sono questi? LETTERE NORMALI? Oh ragazzo! Le lettere sono finalmente trovato e TeX scrive oell
, poi un gruppo di H
( \fi
) si trovano e ampliato (per niente) lasciando il flusso di input con: oellh BHworld Q␣
. Ora la prima parola ha la prima e l'ultima lettera scambiate e ciò che TeX trova dopo è l'altra B
per ripetere l'intero processo per la parola successiva.
}
Alla fine finiamo il gruppo ricominciato lì in modo che tutti gli incarichi locali vengano annullati. Le assegnazioni locali sono i cambiamenti catcode delle lettere A
, B
, C
, ... che sono state fatte delle macro in modo che restituiscano alla loro lettera normale significato e possono essere utilizzati in modo sicuro nel testo. E questo è tutto. Ora la \S
macro definita lì attiverà l'elaborazione del testo come sopra.
Una cosa interessante di questo codice è che è completamente espandibile. Cioè, puoi tranquillamente usarlo per spostare argomenti senza preoccuparti che esploderà. Puoi anche utilizzare il codice per verificare se l'ultima lettera di una parola è uguale alla seconda (per qualsiasi motivo che ti servirà) in 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
Ci scusiamo per la spiegazione prolissa (probabilmente troppo lontana). Ho cercato di renderlo il più chiaro possibile anche per i non TeXies :)
Riepilogo per l'impaziente
La macro \S
antepone l'input con un carattere attivo B
che prende gli elenchi di token delimitati da uno spazio finale e li passa a C
. C
prende il primo token in quell'elenco e lo sposta alla fine dell'elenco di token e si espande D
con ciò che rimane. D
controlla se “ciò che rimane” è vuoto, nel qual caso è stata trovata una parola di una sola lettera, quindi non fa nulla; altrimenti si espande E
. E
scorre l'elenco token fino a quando non trova l'ultima lettera nella parola, quando viene trovata lascia l'ultima lettera, seguita dal centro della parola, seguita dalla prima lettera lasciata alla fine del flusso di token da C
.
Hello, world!
diventa,elloH !orldw
(scambiando punteggiatura come una lettera) ooellH, dorlw!
(mantenendo la punteggiatura in posizione)?