C, 59 bytes
i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}
¡Números mágicos, números mágicos en todas partes!
(Además, ¿C más corto que Python, JS, PHP y Ruby? ¡Inaudito!)
Esta es una función que toma una cadena como entrada y salida a STDOUT.
Tutorial
La estructura básica es:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Aquí, las "cosas dentro" son un montón de código seguido de ,*s++
, donde el operador de coma devuelve solo el valor de su segundo argumento. Por lo tanto, esto se ejecutará a través de la cadena y se establecerá *s
en cada carácter, incluido el byte NUL final (ya que postfix ++
devuelve el valor anterior), antes de salir.
Echemos un vistazo al resto:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Despegando el ternario y cortocircuito ||
, esto se puede expandir a
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
¿De dónde vienen estos números mágicos? Aquí están las representaciones binarias de todos los personajes involucrados:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Primero, necesitamos separar el espacio y NUL del resto de los personajes. La forma en que funciona este algoritmo mantiene un acumulador del número "actual" e imprime cada vez que llega a un espacio o al final de la cadena (es decir '\0'
). Al notar eso ' '
y '\0'
son los únicos caracteres que no tienen ninguno de los dos bits menos significativos establecidos, podemos bit a bit Y el carácter 0b11
para obtener cero si el carácter es espacio o NUL y no cero de lo contrario.
Profundizando, en la primera rama "si", ahora tenemos un personaje que es uno de ellos FBizu
. Elegí solo actualizar el acumulador en F
sy B
s, por lo que necesitaba alguna forma de filtrar el izu
s. Convenientemente, F
y B
ambos tienen solo el segundo, tercer o séptimo conjunto de bits menos significativo, y todos los otros números tienen al menos otro conjunto de bits. De hecho, todos tienen el primer o cuarto bit menos significativo. Por lo tanto, podemos bit a bit AND con 0b00001001
, que es 9, que de lo contrario producirá 0 para F
and B
y no cero.
Una vez que hemos determinado que tenemos un F
o B
, podemos asignarlos a 0
y 1
respectivamente tomando su módulo 5, porque F
es 70
y B
es 66
. Entonces el fragmento
i += i + *s % 5;
es solo una forma de decir golf
i = (i * 2) + (*s % 5);
que también se puede expresar como
i = (i << 1) | (*s % 5);
que inserta el nuevo bit en la posición menos significativa y cambia todo lo demás en 1.
"¡Pero espera!" Podrías protestar. "Después de imprimir i
, ¿cuándo vuelve a restablecerse a 0?" Bueno, putchar
arroja su argumento a un unsigned char
, que resulta ser de 8 bits de tamaño. Eso significa que todo lo que pasa del octavo bit menos significativo (es decir, la basura de las iteraciones anteriores) se descarta, y no debemos preocuparnos por eso.
¡Gracias a @ETHproductions por sugerir reemplazar 57
con 9
, guardando un byte!