TeX, 216 bytes (4 líneas, 54 caracteres cada una)
Debido a que no se trata del conteo de bytes, se trata de la calidad de la salida tipográfica :-)
{\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 }}
Pruébalo en línea! (Al dorso; no estoy seguro de cómo funciona)
Archivo de prueba 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
Salida:
Para LaTeX solo necesitas el repetitivo:
\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}
Explicación
TeX es una bestia extraña. Leer el código normal y comprenderlo es una hazaña en sí mismo. Comprender el código TeX ofuscado va unos pasos más allá. Trataré de hacer esto comprensible para las personas que no conocen TeX también, así que antes de comenzar, aquí hay algunos conceptos sobre TeX para que las cosas sean más fáciles de seguir:
Para principiantes de TeX (no tan)
Primero, y el elemento más importante en esta lista: el código no tiene que estar en forma de rectángulo, a pesar de que la cultura pop podría llevarlo a pensar que sí .
TeX es un lenguaje de macro expansión. Puede, como ejemplo, definir \def\sayhello#1{Hello, #1!}
y luego escribir \sayhello{Code Golfists}
para que TeX imprima Hello, Code Golfists!
. Esto se llama una "macro no delimitada", y para alimentarlo el primer parámetro (y solo, en este caso) lo encierra entre llaves. TeX elimina esas llaves cuando la macro toma el argumento. Puede usar hasta 9 parámetros: \def\say#1#2{#1, #2!}
entonces \say{Good news}{everyone}
.
La contrapartida de macros no delimitada son, como era de esperar, los delimitado :) Se podría hacer que la definición anterior un poco más semántica : \def\say #1 to #2.{#1, #2!}
. En este caso, los parámetros son seguidos por el llamado texto de parámetro . Tal texto parámetro delimita el argumento de la macro ( #1
está delimitada por ␣to␣
, espacios incluidos, y #2
está delimitada por .
). Después de esa definición puedes escribir \say Good news to everyone.
, que se expandirá a Good news, everyone!
. Bien, ¿no es así? :) Sin embargo, un argumento delimitado es (citando el TeXbook ) "la secuencia más corta (posiblemente vacía) de tokens con {...}
grupos correctamente anidados que se sigue en la entrada de esta lista particular de tokens sin parámetros". Esto significa que la expansión de\say Let's go to the mall to Martin
producirá una oración extraña. En este caso lo que se necesita para “ocultar” el primero ␣to␣
con {...}
: \say {Let's go to the mall} to Martin
.
Hasta aquí todo bien. Ahora las cosas comienzan a ponerse raras. Cuando TeX lee un carácter (que se define mediante un "código de carácter"), asigna a ese carácter un "código de categoría" (código de gato, para amigos :) que define lo que significará ese carácter. Esta combinación de caracteres y código de categoría hace un token (más sobre eso aquí , por ejemplo). Los que nos interesan aquí son básicamente:
catcode 11 , que define tokens que pueden formar una secuencia de control (un nombre elegante para una macro). Por defecto, todas las letras [a-zA-Z] son código de gato 11, por lo que puedo escribir \hello
, que es una secuencia de control única, mientras que \he11o
es la secuencia de control \he
seguida de dos caracteres 1
, seguidos de la letra o
, porque 1
no es el código de gato 11. Si yo lo hizo \catcode`1=11
, a partir de ese momento \he11o
sería una secuencia de control. Una cosa importante es que los códigos de gato se establecen cuando TeX ve por primera vez al personaje a mano, y ese código de gato se congela ... ¡PARA SIEMPRE! (Se pueden aplicar términos y condiciones)
catcode 12 , que son la mayoría de los otros caracteres, como, 0"!@*(?,.-+/
etc. Son el tipo de código de gato menos especial, ya que solo sirven para escribir cosas en el papel. Pero oye, ¿quién usa TeX para escribir? (nuevamente, pueden aplicarse términos y condiciones)
catcode 13 , que es el infierno :) En serio. Deja de leer y ve a hacer algo fuera de tu vida. No quieres saber qué es el código de cat. 13. ¿Has oído hablar del viernes 13? ¡Adivina de dónde obtuvo su nombre! ¡Continúa bajo tu propia responsabilidad! Un carácter de código de gato 13, también llamado carácter "activo", ya no es solo un carácter, ¡es una macro en sí misma! Puede definirlo para que tenga parámetros y expandirlo a algo como vimos anteriormente. Después de lo \catcode`e=13
que crees que puedes hacer \def e{I am the letter e!}
, PERO. TÚ. ¡NO PODER! e
ya no es una carta, así \def
que no es lo \def
que sabes, ¡lo es \d e f
! Oh, elige otra letra que dices? ¡Bueno! \catcode`R=13 \def R{I am an ARRR!}
. Muy bien, Jimmy, ¡pruébalo! ¡Me atrevo a hacer eso y escribir un R
en su código! Eso es lo que es un catcode 13. ¡ESTOY CALMADO! Vamonos.
Bien, ahora a la agrupación. Esto es bastante sencillo. Cualesquiera asignaciones ( \def
es una operación de asignación, \let
(entraremos en ella) es otra) que se realizan en un grupo se restauran a lo que eran antes de que ese grupo comenzara a menos que esa asignación sea global. Hay varias formas de iniciar grupos, una de ellas es con los códigos de cat 1 y 2 caracteres (oh, catcodes nuevamente). Por defecto {
es el código de gato 1, o grupo de inicio, y }
es el código de gato 2, o grupo de finalización. Un ejemplo: \def\a{1} \a{\def\a{2} \a} \a
esto imprime 1 2 1
. Fuera del grupo \a
era 1
, a continuación, en su interior se redefinió a 2
, y cuando el grupo terminó, fue restaurado a 1
.
La \let
operación es otra operación de asignación similar \def
, pero bastante diferente. Con \def
usted define macros que se expandirán a cosas, con\let
usted crea copias de cosas ya existentes. Después \let\blub=\def
(el =
es opcional) puede cambiar el inicio del e
ejemplo del elemento del código de cat. 13 anterior \blub e{...
y divertirse con ese. O mejor, en lugar de romper cosas que se pueden arreglar (le mira eso!) Del R
ejemplo: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
. Pregunta rápida: ¿podrías cambiarle el nombre \newR
?
Finalmente, los llamados "espacios espurios". Este es un tema tabú porque hay personas que afirman que la reputación ganada en TeX - LaTeX Stack Exchange al responder preguntas de "espacios espurios" no debe considerarse, mientras que otros están totalmente en desacuerdo. ¿Con quién estás de acuerdo? ¡Haga sus apuestas! Mientras tanto: TeX entiende un salto de línea como un espacio. Intente escribir varias palabras con un salto de línea (no una línea vacía ) entre ellas. Ahora agregue un %
al final de estas líneas. Es como si estuvieras "comentando" estos espacios de fin de línea. Eso es :)
(Más o menos) descifrando el código
Hagamos que ese rectángulo se convierta en algo (posiblemente) más fácil de seguir:
{
\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 }
}
Explicación de cada paso.
Cada línea contiene una sola instrucción. Vamos uno por uno, diseccionándolos:
{
Primero comenzamos un grupo para mantener algunos cambios (es decir, cambios de código de gato) locales para que no alteren el texto de entrada.
\let~\catcode
Básicamente, todos los códigos de ofuscación TeX comienzan con esta instrucción. Por defecto, tanto en TeX como en LaTeX, el ~
carácter es el único carácter activo que puede convertirse en una macro para su uso posterior. Y la mejor herramienta para extrañar el código TeX son los cambios de código de gato, por lo que generalmente es la mejor opción. Ahora en lugar de \catcode`A=13
podemos escribir ~`A13
(el =
es opcional):
~`A13
Ahora la letra A
es un carácter activo, y podemos definirla para hacer algo:
\defA#1{~`#113\gdef}
A
ahora es una macro que toma un argumento (que debería ser otro carácter). Primero, el código de gato del argumento se cambia a 13 para activarlo: ~`#113
(reemplace ~
by \catcode
y agregue an =
y tiene :) \catcode`#1=13
. Finalmente deja un \gdef
(global \def
) en la secuencia de entrada. En resumen, A
activa otro personaje y comienza su definición. Vamos a intentarlo:
AGG#1{~`#113\global\let}
AG
primero "se activa" G
y lo hace \gdef
, que luego sigue el siguiente G
comienza la definición. La definición de G
es muy similar a la de A
, excepto que en lugar de \gdef
eso hace un \global\let
(no hay un \glet
como el \gdef
). En resumen, G
activa un personaje y lo convierte en otra cosa. Hagamos atajos para dos comandos que usaremos más adelante:
GFF\else
GHH\fi
Ahora en lugar de \else
y \fi
simplemente podemos usar F
y H
. Mucho más corto :)
AQQ{Q}
Ahora usamos A
de nuevo para definir otra macro, Q
. La declaración anterior básicamente lo hace (en un lenguaje menos ofuscado) \def\Q{\Q}
. Esta no es una definición terriblemente interesante, pero tiene una característica interesante. A menos que desee romper algún código, la única macro que se expande Q
es a Q
sí misma, por lo que actúa como un marcador único (se llama quark ). Puede usar el \ifx
condicional para probar si el argumento de una macro es tal quark con \ifx Q#1
:
AII{\ifxQ}
así que puedes estar bastante seguro de haber encontrado ese marcador. Tenga en cuenta que en esta definición eliminé el espacio entre \ifx
y Q
. Por lo general, esto llevaría a un error (tenga en cuenta que el resaltado de sintaxis cree que \ifxQ
es una cosa), pero como ahora Q
es el código de cat. 13, no puede formar una secuencia de control. Sin embargo, tenga cuidado de no expandir este quark o se quedará atrapado en un bucle infinito porque se Q
expande aQ
que se expande a lo Q
que ...
Ahora que los preliminares están terminados, podemos ir al algoritmo apropiado para establecer la configuración. Debido a la tokenización de TeX, el algoritmo debe escribirse al revés. Esto se debe a que en el momento en que realiza una definición, TeX tokenizará (asignará códigos de barras) a los caracteres de la definición utilizando la configuración actual, por ejemplo, si lo hago:
\def\one{E}
\catcode`E=13\def E{1}
\one E
la salida es E1
, mientras que si cambio el orden de las definiciones:
\catcode`E=13\def E{1}
\def\one{E}
\one E
la salida es 11
. Esto se debe a que en el primer ejemplo, E
en la definición se tokenizó como una letra (catcode 11) antes del cambio de catcode, por lo que siempre será una letra E
. Sin embargo, en el segundo ejemplo, E
primero se activó, y solo entonces \one
se definió, y ahora la definición contiene el código de cat. 13E
que se expande a 1
.
Sin embargo, pasaré por alto este hecho y reordenaré las definiciones para que tengan un orden lógico (pero que no funcione). En los siguientes párrafos se puede asumir que las letras B
, C
, D
, y E
son activos.
\gdef\S#1{\iftrueBH#1 Q }
(observe que había un pequeño error en la versión anterior, no contenía el espacio final en la definición anterior. Solo lo noté mientras escribía esto. Siga leyendo y verá por qué necesitamos ese para terminar correctamente la macro. )
En primer lugar se define la macro-nivel de usuario, \S
. Este no debe ser un personaje activo para tener una sintaxis amigable (?), Por lo que la macro para gwappins eht setterl es \S
. La macro comienza con un condicional siempre verdadero \iftrue
(pronto quedará claro por qué), y luego llama a la B
macro seguida de H
(que definimos anteriormente como \fi
) para que coincida con \iftrue
. Luego dejamos el argumento de la macro #1
seguido de un espacio y el quark Q
. Supongamos que usamos \S{hello world}
, luego el flujo de entradadebería verse así: \iftrue BHhello world Q␣
(Reemplacé el último espacio por un␣
para que la representación del sitio no lo coma, como hice en la versión anterior del código). \iftrue
es cierto, entonces se expande y nos quedamos BHhello world Q␣
. TeX no no retire la \fi
( H
) después de evaluar el condicional, en cambio, deja allí hasta que el \fi
está realmente expandieron. Ahora la B
macro se expande:
ABBH#1 {HI#1FC#1|BH}
B
es una macro delimitada cuyo texto de parámetro es H#1␣
, por lo que el argumento es lo que sea que esté entre H
y un espacio. Continuando con el ejemplo anterior a la secuencia de entrada antes de la expansión de B
is BHhello world Q␣
. B
es seguido por H
, como debería (de lo contrario, TeX generaría un error), entonces el siguiente espacio está entre hello
y world
, también lo #1
es la palabra hello
. Y aquí tenemos que dividir el texto de entrada en los espacios. Yay: D La expansión de B
quita todo hasta el primer espacio de la corriente de entrada y la sustituye por HI#1FC#1|BH
la #1
de serhello
: HIhelloFChello|BHworld Q␣
. Observe que hay un nuevo BH
más adelante en la secuencia de entrada, para hacer una recursión de cola deB
y procesar palabras posteriores. Después de que esta palabra se procesa, B
procesa la siguiente palabra hasta que la palabra a procesar sea el quark Q
. Se Q
necesita el último espacio después porque la macro delimitada B
requiere uno al final del argumento. Con la versión anterior (ver historial de edición) el código se comportaría mal si lo usara \S{hello world}abc abc
(el espacio entre los abc
s se desvanecería).
OK, de vuelta a la corriente de entrada: HIhelloFChello|BHworld Q␣
. Primero está el H
( \fi
) que completa la inicial \iftrue
. Ahora tenemos esto (pseudocodificado):
I
hello
F
Chello|B
H
world Q␣
El I...F...H
pensar es en realidad una \ifx Q...\else...\fi
estructura. La \ifx
prueba verifica si la (primera señal de) la palabra tomada es el Q
quark. Si es que no hay nada más que hacer y las termina la ejecución, de lo contrario lo que queda es: Chello|BHworld Q␣
. Ahora C
se expande:
ACC#1#2|{D#2Q|#1 }
El primer argumento de C
que se no delimitada, por lo menos se preparó será de un solo símbolo, el segundo argumento está delimitado por |
, por lo que después de la expansión de C
(con #1=h
y #2=ello
) del flujo de entrada es: DelloQ|h BHworld Q␣
. Observe que |
se coloca otro , y el h
de hello
se pone después de eso. La mitad del intercambio está hecho; La primera letra está al final. En TeX es fácil tomar el primer token de una lista de tokens. Una macro simple \def\first#1#2|{#1}
obtiene la primera letra cuando la usa \first hello|
. El último es un problema porque TeX siempre toma la lista de tokens "más pequeña, posiblemente vacía" como argumento, por lo que necesitamos algunas soluciones. El siguiente elemento en la lista de tokens es D
:
ADD#1#2|{I#1FE{}#1#2|H}
Esta D
macro es una de las soluciones y es útil en el único caso donde la palabra tiene una sola letra. Supongamos que en lugar de hello
tener x
. En este caso el flujo de entrada sería DQ|x
, entonces D
se ampliaría (con #1=Q
, y #2
vaciar) a: IQFE{}Q|Hx
. Esto es similar al bloque I...F...H
( \ifx Q...\else...\fi
) B
, que verá que el argumento es el quark e interrumpirá la ejecución dejando solo la x
composición tipográfica. En otros casos (volviendo al hello
ejemplo), D
se expandiría (con #1=e
y #2=lloQ
) a: IeFE{}elloQ|Hh BHworld Q␣
. Una vez más, la I...F...H
comprobará si hay Q
pero se producirá un error y tomar la \else
rama: E{}elloQ|Hh BHworld Q␣
. Ahora la última pieza de esta cosa, elE
la macro se expandiría:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
El texto del parámetro aquí es bastante similar a C
y D
; los argumentos primero y segundo no están delimitados, y el último está delimitado por |
. El aspecto de flujo de entrada como esta: E{}elloQ|Hh BHworld Q␣
, a continuación, E
se expande (con #1
vacío, #2=e
y #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Otro de I...F...H
los controles de bloque para el quark (que ve l
y regresa false
): E{e}lloQ|HHh BHworld Q␣
. Ahora E
se expande de nuevo (con #1=e
vacío, #2=l
y #3=loQ
): IloQleFE{el}loQ|HHHh BHworld Q␣
. Y otra vez I...F...H
. La macro hace algunas iteraciones más hasta Q
que finalmente se encuentra y true
se toma la rama: E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> IQoellFE{ello}Q|HHHHHh BHworld Q␣
. Ahora el quark se encuentra y se expande condicionales a: oellHHHHh BHworld Q␣
. Uf.
Oh, espera, ¿qué son estos? CARTAS NORMALES? ¡Oh chico! Las letras son finalmente encontraron y Tex escribe oell
, entonces un montón de H
( \fi
) se encuentran y se expande (o nada) que sale de la corriente de entrada con: oellh BHworld Q␣
. Ahora la primera palabra tiene la primera y la última letra intercambiadas y lo que TeX encuentra a continuación es la otra B
para repetir todo el proceso para la siguiente palabra.
}
Finalmente terminamos el grupo que comenzó allí para que se deshagan todas las tareas locales. Las tareas locales son los cambios catcode de las letras A
, B
, C
, ... que se hicieron las macros para que vuelvan a su significado normal letra y pueden ser utilizados con seguridad en el texto. Y eso es. Ahora la \S
macro definida allí activará el procesamiento del texto como se indicó anteriormente.
Una cosa interesante sobre este código es que es completamente expandible. Es decir, puede usarlo de manera segura para mover argumentos sin preocuparse de que explote. Incluso puede usar el código para verificar si la última letra de una palabra es la misma que la segunda (por cualquier razón que lo necesite) en una \if
prueba:
\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
Perdón por la explicación (probablemente demasiado) prolija. Traté de hacerlo lo más claro posible para los que no son TeXies también :)
Resumen para los impacientes
La macro \S
antepone la entrada con un carácter activo B
que toma listas de tokens delimitados por un espacio final y los pasa a C
. C
toma el primer token en esa lista y lo mueve al final de la lista de tokens y se expande D
con lo que queda. D
comprueba si "lo que queda" está vacío, en cuyo caso se encontró una palabra de una sola letra, luego no hace nada; de lo contrario se expande E
. E
recorre la lista de tokens hasta que encuentra la última letra de la palabra, cuando se encuentra, deja esa última letra, seguida de la mitad de la palabra, que luego es seguida por la primera letra que queda al final del flujo de tokens C
.
Hello, world!
se convierte,elloH !orldw
(intercambiando la puntuación como una letra) ooellH, dorlw!
(manteniendo la puntuación en su lugar)?