C, 59 bajtów
i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}
Magiczne liczby, magiczne liczby wszędzie!
(Również C jest krótszy niż Python, JS, PHP i Ruby? Niespotykane!)
Jest to funkcja, która pobiera ciąg jako dane wejściowe i wyjściowe do STDOUT.
Przewodnik
Podstawowa struktura to:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Tutaj „rzeczy w środku” to wiązka kodu, po ,*s++
której operator przecinka zwraca tylko wartość drugiego argumentu. Dlatego też przejdzie przez ciąg i ustawi *s
każdy znak, w tym końcowy bajt NUL (ponieważ postfix ++
zwraca poprzednią wartość) przed wyjściem.
Rzućmy okiem na resztę:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Odrywając trójskładnikowe i zwarciowe ||
, można to rozszerzyć do
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
Skąd pochodzą te magiczne liczby? Oto reprezentacje binarne wszystkich zaangażowanych postaci:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Najpierw musimy oddzielić spację i NUL od reszty postaci. Sposób działania tego algorytmu utrzymuje akumulator o numerze „bieżącym” i wypisuje go, gdy tylko dojdzie do spacji lub końca łańcucha (tj '\0'
.). Zauważając to ' '
i '\0'
jesteśmy jedynymi znakami, które nie mają ustawionego żadnego z dwóch najmniej znaczących bitów, możemy bitowo ORAZ znak za pomocą, 0b11
aby uzyskać zero, jeśli znak jest spacją lub NUL i w przeciwnym razie jest niezerowy.
Kopiąc głębiej, w pierwszej gałęzi „jeśli” mamy teraz jedną z postaci FBizu
. Zdecydowałem się tylko zaktualizować akumulator na F
si B
s, więc potrzebowałem jakiegoś sposobu, aby odfiltrować izu
s. Dogodnie F
i B
oba mają tylko drugi, trzeci lub siódmy najmniej znaczący zestaw bitów, a wszystkie pozostałe liczby mają co najmniej jeden inny zestaw bitów. W rzeczywistości wszystkie mają pierwszy lub czwarty najmniej znaczący bit. Stąd możemy bitowo ORAZ z 0b00001001
, która wynosi 9, co da 0 dla F
i, w B
przeciwnym razie, zero.
Po ustaleniu, że mamy F
lub B
, możemy zmapować je odpowiednio na 0
i 1
, biorąc ich moduł 5, ponieważ F
jest 70
i B
jest 66
. Następnie fragment kodu
i += i + *s % 5;
to po prostu golfowy sposób na powiedzenie
i = (i * 2) + (*s % 5);
co można również wyrazić jako
i = (i << 1) | (*s % 5);
który wstawia nowy bit w co najmniej znaczącej pozycji i przesuwa wszystko inne o 1.
"Ale poczekaj!" możesz protestować. „Po wydrukowaniu i
, kiedy w ogóle resetuje się do 0?” Cóż, putchar
rzuca swój argument na an unsigned char
, który akurat ma 8 bitów. Oznacza to, że wszystko po 8. najmniej znaczącym bicie (tj. Śmieci z poprzednich iteracji) jest wyrzucane i nie musimy się tym martwić.
Dzięki @ETHproductions za sugerowanie zastąpić 57
z 9
, oszczędzając bajt!