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 em todos os lugares!
(Além disso, C menor que Python, JS, PHP e Ruby? Inédito!)
Esta é uma função que recebe uma string como entrada e sai para STDOUT.
Passo a passo
A estrutura básica é:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Aqui, o "material interno" é um monte de código seguido por ,*s++
, em que o operador de vírgula retorna apenas o valor do seu segundo argumento. Portanto, isso percorrerá a cadeia e definirá *s
todos os caracteres, incluindo o byte NUL à direita (já que o postfix ++
retorna o valor anterior), antes de sair.
Vamos dar uma olhada no resto:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Retirando o circuito ternário e curto-circuito ||
, isso pode ser expandido para
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
De onde vêm esses números mágicos? Aqui estão as representações binárias de todos os personagens envolvidos:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Primeiro, precisamos separar espaço e NUL do resto dos personagens. Da maneira como esse algoritmo funciona, ele mantém um acumulador do número "atual" e o imprime sempre que atinge um espaço ou o final da string (ou seja '\0'
). Ao perceber que ' '
e '\0'
são os únicos caracteres que não possuem nenhum dos dois bits menos significativos definidos, podemos AND bit a bit e 0b11
obter o zero se o caractere for espaço ou NUL e diferente de zero.
Indo mais fundo, no primeiro ramo "se", agora temos um personagem que é um deles FBizu
. Eu escolhi apenas atualizar o acumulador em F
s e B
s, então eu precisava de uma maneira de filtrar os izu
s. Convenientemente, F
e B
ambos têm apenas o segundo, terceiro ou sétimo bits menos significativos definidos, e todos os outros números têm pelo menos um outro conjunto de bits. De fato, todos eles têm o primeiro ou o quarto bit menos significativo. Assim, podemos bit a bit E com 0b00001001
, o qual é 9, o que vai originar 0 para F
e B
e diferente de zero em contrário.
Depois de determinarmos que temos um F
ou B
, podemos mapeá-los para 0
e 1
respectivamente, tomando seu módulo 5, porque F
é 70
e B
é 66
. Então o trecho
i += i + *s % 5;
é apenas uma maneira de dizer golfe
i = (i * 2) + (*s % 5);
que também pode ser expresso como
i = (i << 1) | (*s % 5);
que insere o novo bit na posição menos significativa e muda todo o resto em 1.
"Mas espere!" você pode protestar. "Depois de imprimir i
, quando é que ele é redefinido para 0?" Bem, putchar
lança seu argumento para an unsigned char
, que por acaso tem 8 bits de tamanho. Isso significa que tudo que passou do oitavo bit menos significativo (ou seja, o lixo das iterações anteriores) é jogado fora, e não precisamos nos preocupar com isso.
Agradecemos a @ETHproductions por sugerir a substituição 57
por 9
, salvando um byte!