Число байтов предполагает кодировку ISO 8859-1.
+%`\B
¶$`:
1
Попробуйте онлайн!
Альтернативное решение:
+1`\B
:$`:
1
объяснение
Это, вероятно, будет легче объяснить, основываясь на моей старой версии, в которой меньше возможностей для игры в гольф, а затем покажу, как я ее сократил. Я использовал для преобразования двоичного в десятичное, как это:
^
,
+`,(.)
$`$1,
1
Единственный разумный способ построить десятичное число в Retina - это подсчитать вещи (потому что Retina имеет несколько функций, которые позволяют печатать десятичное число, представляющее сумму). Так что на самом деле единственный возможный подход - преобразовать двоичный код в унарный, а затем посчитать количество унарных цифр. Последняя строка выполняет подсчет, поэтому первые четыре преобразуют двоичный код в унарный.
Как мы это делаем? В общем, чтобы преобразовать список битов в целое число, мы инициализируем результат 0
и затем перебираем биты от старшего к младшему, удваиваем значение, которое у нас уже есть, и добавляем текущий бит. Например, если двоичное число есть 1011
, мы действительно вычислим:
(((0 * 2 + 1) * 2 + 0) * 2 + 1) * 2 + 1 = 11
^ ^ ^ ^
Где я отметил отдельные биты для ясности.
Хитрость сделать это в унарном виде состоит в том, что а) что удвоение означает просто повторение числа и б), так как мы считаем 1
s в конце, нам даже не нужно различать 0
s и 1
s в процессе. Это станет яснее через секунду.
Что программа делает, так это то, что она сначала добавляет запятую в начало в качестве маркера того, сколько входных данных мы уже обработали:
^
,
Слева от маркера у нас будет значение, которое мы накапливаем (которое правильно инициализируется равным унарному представлению нуля), а справа от значения будет следующий бит для обработки. Теперь мы применяем следующую замену в цикле:
,(.)
$`$1,
Просто глядя на ,(.)
и $1,
, это каждый раз перемещает маркер на один бит вправо. Но мы также вставляем $`
, то есть все перед маркером, то есть текущее значение, которое мы удваиваем. Вот отдельные шаги при обработке ввода 1011
, где я пометил результат вставки $`
над каждой строкой (он пуст для первого шага):
,1011
1,011
_
110,11
___
1101101,1
_______
110110111011011,
Вы увидите, что мы сохранили и удвоили ноль вместе со всем остальным, но так как мы игнорируем их в конце, не имеет значения, как часто мы удваивали их, пока число 1
s правильный. Если вы посчитаете их, 11
их будет просто то, что нам нужно.
Таким образом, остается вопрос о том, как сделать это до 12 байтов. Самая дорогая часть 18-байтовой версии - использование маркера. Цель состоит в том, чтобы избавиться от этого. Мы действительно хотим удвоить префикс каждого бита, поэтому первая идея может быть такой:
.
$`$&
Проблема в том, что эти замены происходят одновременно, поэтому первый бит не удваивается для каждого бита, а просто копируется один раз каждый раз. Для ввода 1011
мы получим (пометив вставленный $`
):
_ __ ___
1101011011
Нам по-прежнему необходимо рекурсивно обрабатывать ввод, чтобы удвоенный первый префикс снова удваивался со второго и так далее. Одна идея состоит в том, чтобы вставлять маркеры везде и многократно заменять их префиксом:
\B
,
+%`,
¶$`
После замены каждого маркера префиксом в первый раз, нам нужно запомнить, где было начало ввода, поэтому мы также вставляем перевод строки и используем %
опцию, чтобы убедиться, что следующий $`
выбирает только ближайший перевод строки.
Это работает, но это все еще слишком долго (16 байт при подсчете 1
s в конце). Как насчет того, чтобы все перевернуть? Места, в которые мы хотим вставить маркеры, обозначены \B
(позиция между двумя цифрами). Почему бы нам просто не вставить префиксы в эти позиции? Это почти работает, но разница в том, что в предыдущем решении мы фактически удалили один маркер в каждой замене, и это важно, чтобы процесс завершился. Тем не менее, \B
это не персонаж, а просто позиции, поэтому ничего не удаляется. Однако мы можем остановить\B
от сопоставления, вместо этого вставив нецифровый символ в это место. Это превращает несловесную границу в границу слова, что эквивалентно удалению символа маркера ранее. И вот что делает 12-байтовое решение:
+%`\B
¶$`:
Просто для полноты, вот отдельные этапы обработки 1011
, с пустой строкой после каждого шага:
1
1:0
10:1
101:1
1
1:0
1
1:0:1
1
1:0
10:1:1
1
1:0
1
1:0:1
1
1:0
1
1:0:1:1
Опять же, вы обнаружите, что последний результат содержит ровно 11 1
с.
В качестве упражнения для читателя, вы можете увидеть, как это довольно просто обобщается на другие базы (для нескольких дополнительных байтов на инкремент в базе)?