C, 59 байт
i;f(char*s){while(*s&3?*s&9||(i+=i+*s%5):putchar(i),*s++);}
Чарівні числа, чарівні числа скрізь!
(Також C коротше, ніж Python, JS, PHP та Ruby? Нечувано!)
Це функція, яка приймає рядок як вхід і виводить в STDOUT.
Покрокова інструкція
Основна структура:
i; // initialize an integer i to 0
f(char*s){
while(...); // run the stuff inside until it becomes 0
}
Тут «речі всередині» - це купа коду, за яким ,*s++
оператор з комами повертає лише значення другого аргументу. Отже, це буде проходити через рядок і встановлюватись *s
для кожного символу, включаючи кінцевий байт NUL (оскільки постфікс ++
повертає попереднє значення) перед виходом.
Давайте подивимось на решту:
*s&3?*s&9||(i+=i+*s%5):putchar(i)
Знімаючи трійку і коротке замикання ||
, це можна розширити до
if (*s & 3) {
if (!(*s & 9)) {
i += i + *s % 5;
}
} else {
putchar(i);
}
Звідки беруться ці магічні числа? Ось двійкові зображення всіх залучених персонажів:
F 70 01000110
B 66 01000010
i 105 01101001
z 122 01111010
u 117 01110101
32 00100000
\0 0 00000000
Спочатку нам потрібно відокремити простір та NUL від решти символів. Як працює цей алгоритм, він зберігає акумулятор "поточного" числа і друкує його щоразу, коли він досягає пробілу або кінця рядка (тобто '\0'
). Зауваживши, що ' '
і '\0'
є єдиними символами, які не мають жодного з двох найменш значущих бітів, ми можемо побітовим І символом з, 0b11
щоб отримати нуль, якщо символ є пробілом або NUL, а не нульовим значенням є інше.
Копаючи глибше, у першій «якщо» гілці, тепер у нас є персонаж, який є одним із FBizu
. Я вибрав лише оновлення акумулятора на F
s і B
s, тому мені знадобився якийсь спосіб відфільтрувати izu
s. Зручно, F
і B
обидва мають лише другий, третій чи сьомий найменш значущі біти, а всі інші числа мають принаймні один інший біт. Насправді всі вони мають або перший, або четвертий найменш значущий біт. Отже, ми можемо побіжно AND з 0b00001001
, що дорівнює 9, що дасть 0 для F
і, B
а не нульове значення в іншому випадку.
Як тільки ми визначимо, що у нас є F
або B
, ми можемо відобразити їх відповідно 0
і 1
, взявши їх модуль 5, тому що F
є 70
і B
є 66
. Потім фрагмент
i += i + *s % 5;
- це просто грізний спосіб сказати
i = (i * 2) + (*s % 5);
що також можна виразити як
i = (i << 1) | (*s % 5);
який вставляє новий біт принаймні значущому положенні та зміщує все інше на 1.
"Але чекай!" ви можете протестувати. "Коли ви надрукуєте i
, коли це колись повернеться до 0?" Що ж, putchar
приводить свій аргумент до unsigned char
величини, яка так само буває, що має 8 біт. Це означає, що все, що минуло восьмий найменш значущий біт (тобто мотлох від попередніх ітерацій), викидається, і нам не потрібно про це турбуватися.
Завдяки @ETHproductions для пропонуючи замінити 57
з 9
, зберігаючи байт!