C, 59 byte
i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}
Numeri magici, numeri magici ovunque!
(Inoltre, C più corto di Python, JS, PHP e Ruby? Inaudito!)
Questa è una funzione che accetta una stringa come input e output su STDOUT.
Procedura dettagliata
La struttura di base è:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Qui, "stuff inside" è un gruppo di codice seguito da ,*s++
, in cui l'operatore virgola restituisce solo il valore del suo secondo argomento. Quindi, questo verrà eseguito attraverso la stringa e impostato *s
su ogni carattere, incluso il byte NUL finale (poiché postfix ++
restituisce il valore precedente), prima di uscire.
Diamo un'occhiata al resto:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Staccando il ternario e il corto circuito ||
, questo può essere espanso
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
Da dove vengono questi numeri magici? Ecco le rappresentazioni binarie di tutti i personaggi coinvolti:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Innanzitutto, dobbiamo separare lo spazio e NUL dal resto dei personaggi. Il modo in cui funziona questo algoritmo mantiene un accumulatore del numero "corrente" e lo stampa ogni volta che raggiunge uno spazio o la fine della stringa (cioè '\0'
). Notando che ' '
e '\0'
sono gli unici caratteri a non avere nessuno dei due bit meno significativi impostati, possiamo bit a bit E il carattere con 0b11
ottenere zero se il carattere è spazio o NUL e diverso da zero.
Scavando più a fondo, nel primo ramo "if", ora abbiamo un personaggio di cui è uno FBizu
. Ho scelto solo di aggiornare l'accumulatore su F
s e B
s, quindi avevo bisogno di un modo per filtrare izu
s. Convenientemente, F
ed B
entrambi hanno solo il secondo, il terzo o il settimo bit meno significativo impostato, e tutti gli altri numeri hanno almeno un altro bit impostato. In effetti, tutti hanno il primo o il quarto bit meno significativo. Quindi, possiamo bit a bit AND con 0b00001001
, che è 9, che produrrà 0 per F
e B
e diverso da zero.
Una volta stabilito che abbiamo un F
o B
, possiamo mapparli 0
e 1
rispettivamente prendendo il loro modulo 5, perché F
è 70
ed B
è 66
. Quindi lo snippet
i += i + *s % 5;
è solo un modo di dire golfy
i = (i * 2) + (*s % 5);
che può anche essere espresso come
i = (i << 1) | (*s % 5);
che inserisce il nuovo bit nella posizione meno significativa e sposta tutto il resto su 1.
"Ma aspetta!" potresti protestare. "Dopo la stampa i
, quando viene ripristinato su 0?" Bene, putchar
lancia il suo argomento a un unsigned char
, che in questo caso ha una dimensione di 8 bit. Ciò significa che tutto ciò che è passato dall'ottavo bit meno significativo (ovvero la spazzatura delle precedenti iterazioni) viene eliminato e non dobbiamo preoccuparci.
Grazie a @ETHproductions per aver suggerito di sostituire 57
con 9
, salvando un byte!