Это было веселое испытание - работать и выключать между рождественскими праздниками. Спасибо за публикацию! Игра в гольф была интересной, потому что спецификация полна исключений и особых случаев, которые требовали много условий. Кроме того, хотя в этот раз мне не нужно было выполнять преобразование в десятичное число и обратно, мне нужна была своего рода функция «max» для определения наибольшего числа цифр в каждом числе и наибольшего значения цифр в каждом месте.
Первая версия этого была 4844 байта, просто чтобы дать вам представление о том, сколько я играл в гольф.
Программа ожидает ввод в виде списка целых чисел через запятую . Без пробелов и переносов. Их использование приведет к неопределенному поведению.
«" „“ „“»«»«„“»«»«„“»«»«„“ „„„“ „“ „“““ „“»«»«„“»«»«„“» «» «„“» «» «„“» «» «„“» «» «» «» «„“» «» «„“» «» «„“» «» "„“" „„““ «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» '«» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «„“» «» «» «» "» «» «» «» «„„“ „„„“““ „„„“““ „„„“““ „“ „“ „„„“““ „““» «„„““» "» «» «„“» «» «„“» «» «„“„„„“„“„„„“„“„“““„“““„“„“„“"»„“„“«» «" „“ „„„“ „“ „“““ „“ „“ „“»«„“ „„„“““ "» „“ „“ „“ "" „“ „“ „“ „“ «" „„„“ „“ „„„“““ „“ „“ „„„“““ „„„“““ „“““ „“ „“ „“ „“ "„„“ „““ «» «» «» «„“» «» «„“"»„“„“„“„„„“““„„„“““„„„“““«"„“» ""«» «» «» «» «„“„“„“» «» «» «» «„“» «» «„„““» «» «» «» «„“» «» "" „“ «" „“ „„„“ „“ „“““ „„„“““ „“»«»«»«»«»«„“ „“ „“ „„„“““ "» „“ " «" „“ „“»«»«„“»«»«„“ „„„“ „“ „“ „“““ „“ „“ „“»«»«„“»«»"»«» «„“„“„“» «» «» «» «„“„„„“„“„“„“““„“„“„“» «» «" „“»" „“ „“ «» «„“„„„“„“„“„“““„“» «» «» «» «„“» «» «„“"»„“„“«"„“„“„“» «" „“ „“ „„„“ „„„“ „“ „“““ „“““ „“ „“ „“ "» „“ „“ „“ «" „“»" „“«» «„„“ „“ „““» «» «„“» «» «» «» «" „“ „„„“ „“““ „“ „“ „„„“““»" „“ «" „„„“ „“ „“ „“““ „“»«»«»«„„“„“„“„““»«»«»«"„“„“„“» "„“ «„„““» «» «» «» «„“„“„“"»„“«"„“„“„“„“» «„“„“„“„“"»„“«» «» «" „“ „“ „“»" „“ „„„“ „„„“ „“ „“ „“ „„„“““ „“ „“ „“““ „“““ „“ «" „“ „“ „“»«»«„„“„““»«»«„„“„“„““»«"„“„„„“““„„„“““» " «» «„“» «» «„“» «» «" „“ „“»«„“ „“ „“ „„„“ „“ „“““ "» „“ „„„“““«» «» «» «„“» «» «„„“ „““» «» "" „“ „“ „„„“ „“ „“““ „„„“““ „„„“““ «„„““» «„„“ „„„“ „“ „“““ „„„“““ „„„“““ „““» «» «» «» «„“„“„“» «» «„“„„„“““„“„“„“» «» «„“"»„“«"„“„“» «„“„“„“"»„“„“"«„„““» «» «„“„“„“„“» «» «» «» «„„““» «» «„“„“„“„“"»„“„“„“" «" „“ „“ „“ „„„“ „“ „“““ „„„“““ „„„“““ „„„“““»«„“ "» „“ „“ „“ „“ «" „“ „“ „„„“““ „„„“““ „„„“““ „„„“““ „“»«»«»«»«»" "„“„“«» «» «„“„“„“„“» «» «» «» «" „“ „“ „“»«„“ "» „“ „“ „“ „“ "" „“ „“ „“ «„„““» «„„““» «» «„“„“„“» «» «„“» «» «„“» «» «"»«„“ „“ „“ „“ "» «» «„“„“„“„„„“„“„“„„„“““„“““„“„“„“„“» «» «„“» «» «»»«» «„“„“„“„„„“““„“» «» «» «„“„“„“» «» «» «» «„„““» «» "„“„“» «» «» «„“„“„“„“„„„“““„“„“„“» «» «" „“»«„“ „“ „“ „“ „„„“““ "» «» «» «» «" „“ „“ „“»«„“ "» „“ «" „“ „“ „“ „“ „„„“““ „„„“““ „“»«» «„„“ „“ „“ „„„“““ „““» «» «„“„“„“„“» «» «„„““» «» «» «» "„“» «» «» «„“» «» «„„“ „“ „“ „““» «» «» «» «" „“»«„“ „“ „“ "» „“ „“ „“ „“ «" "» „“ „“ „“ „“ «" „“ „“ „“»«„“ „“ „“ "» „“ «" „“ „“ „“ „“»" „“«" „“ „“ "» „“ «" „“ „“ „“ „„„“““ „„„“““ „„„“““»«„“ „„„“““ "» " «» «» «» «„“» «» «„“» «» «„“» «» «„„“ „“ „“ „„„“ „“ „“ „“““ „““» «» «» «» «„“» «» «„“» «» «„“» «» «„„“ „““» «» «» «» «» "„“„“„“„“»«"»«»«»«„„“„“„“„„„“„“„“““„„„“““„““»«»«»«»" "„“„“„“ «„„““» «„„“ „„„“ „“ „“““ „“ „“ „““» "" „“ „„„“ „“ „“ „“““ „“ „“ „“ «" „“ „“ „„„“““ „“»«»«"„“„“„“„“» "„“„“„“„„„“““„„„“““«„„““» «» «» «» «„“» «» «„“"»„“„„„“„“„“„“„„„“““„“„“„“““" «„„“ „„„“ „“ „“ „“““ „““» «» «» «„„“ „“ „“ „„„“““ „““» «» «» «» "» «» «» «» «„„“ „“ „“ „„„“““ „““» «» «„“„“"»„“„“„“" "„“„“„“«"»«»«„“ „„„“““ „“ „“ „“»«»«„“»«»«"„“„„„“„“““„“» "„“„“ «„„“ „“ „““» «» «» «» «„“» «» «„„“ „“ „“ „““» «» «» «» «„“„“„“"»" «" „“ „„„“ „“ „“““ „“ „“ „„„“““ „„„“ „„„“ „“ „“ „“““ „“ „“ „“““«„„““» «» «„“"»„“„“„„„“„“„“„„„“„“„“““„„„“““„“““«"„“» «» «» «„“» «» «„“„„„“„“„“““„“„“„“» «» «„“„„„“““„“„“„“» «» «» «» «„„““» «» «„“„“„“„“„„„“““„“„“„“» «» «„„“ „“ „““» «»«» «» «„„“ „“ „“ „““» «» «» «» «„„“ „“ „“ „““» «» «» «» «„“» «» "„“» «" "» „“ «" „“»«„“ „“ „“ „“ "» „“ „“ «" „“ „“ „“ „„„“ „“ „“““»" „“ " «"»«»«»«»«„„““»«»«„“ „“ „“ „„„“““ „„„“““ "» „“ «" „“»" "«» «» «» «» «„“„“„“„„„“““„„„“““„“» "" „“ „“ „“ „„„“ „“ „“ „“““ «» «„“"»„“„“„“„“„„„“„“„“„“““„“„“„“„„„“„“„“„“““„“„“" «" „“ „“ „“»" „“ "„„“ „“ „“ „“ „„„“ „“ „“ „„„“““ „„„“““ „““““«» «„“» «» «„“» «» «„“» «» «„“» «» "„“" „„“„„„“„“„“““„“„“„““ «» «„„“ „“ „“ „““» «» «» «„„““» «„„““» «» «„“„“"»„“„“„“«"„“» «» «„„““» «„„“ „„„“ „“ „“ „“““ „“ „“ „““» «„„““» «» «„“„“„“»«» «» «» «„„“ „„„“ „“ „“ „“““ „“ „“ „““» «» «» «» «„“"»„“"
объяснение
Я расскажу вам, как работает программа, и покажу, как она обрабатывает определенные данные 202,100,1
.
В начале мы создадим несколько значений, которые нам понадобятся позже - в основном это ASCII-коды символов, которые мы будем выводить.
Как видите, '8'
и'.'
уже есть в наличии. '|'
однако в действительности это 124, а не 14. Мы используем цикл while, чтобы дважды добавить к нему временное значение в слоте № 1, чтобы получить 124 (что составляет 14 + 55 × 2, потому что цикл while работает для 56−1 = 55 итерации). Это экономит некоторые байты, потому что большие целочисленные литералы, такие как 124, действительно длинные. На следующей диаграмме я показываю расположение каждой переменной, используемой программой.
Далее мы хотим ввести все символы и сохранить их на ленте, начиная с ячейки # 12 ( p - это текущий указатель для этого). В то же время, мы хотим знать, какой длины самое длинное число (сколько цифр). Чтобы достичь этого, мы сохраняем промежуточную сумму в унарном порядке, идущем влево, начиная с ячейки # -1 (мы используем q в качестве бегущего указателя). После первого ввода числа ( 202
) лента теперь выглядит так:
Вы заметите, что числа отключены на 4. Хорошо, когда мы впервые вводим их, они являются их значениями ASCII, поэтому они «выключены» на 48 и запятая составляет 44. Для каждого символа мы копируем 46 из '.'
в r и затем вычитаем его с помощью цикла while (который вычитает 45), а затем добавляем 1. Мы делаем это так, что запятая (наш разделитель) равна 0, поэтому мы можем использовать условное выражение для его распознавания.
Кроме того, вы заметили, что мы оставляем ячейку № 11 в 0. Нам нужно это, чтобы распознать границу первого числа.
Следующим символом будет запятая, поэтому мы храним 0 в # 15, но, конечно, на этот раз мы не продвигаем q . Вместо этого мы устанавливаем q обратно в 0 и начинаем «перезаписывать» те 1, которые мы уже разместили.
После того, как все оставшиеся символы обработаны, мы получаем это:
Как вы можете видеть, 1s, написанные q, теперь указывают (в унарном виде) длину самого длинного числа.
Теперь мы используем цикл while для перемещения q в крайнее левое положение, а затем помещаем туда другой указатель, который я назову r2 . Цель r2 станет ясна позже.
На этом этапе позвольте мне уточнить терминологию, которую я буду использовать на протяжении всего этого.
- По номером я подразумеваю одно из входных чисел, разделенных запятыми. В нашем примере это 202, 100 и 1.
- По цифрой я подразумеваю одну цифру в конкретном из чисел. Первое число имеет 3 цифры.
- По месту я подразумеваю «одно место», «десятки», «сотни» и т. Д. Поэтому, если я скажу «цифры в текущем месте», а текущее место - это «те места», эти цифры равны 2, 0 и 1 в этом месте. приказ.
Теперь вернемся к нашему обычному программированию. Вся остальная часть программы представляет собой большой цикл, который перемещает q вперед, пока не достигнет ячейки # 0. Каждая из ячеек на этом пути представляет собой место, где ячейки расположены справа, и q будет начинаться с самого значительного. В нашем примере это сотни мест.
Мы продолжаем, увеличивая ячейку q на (то есть * q ).
Сейчас мы находимся на «этапе 2» для сотен мест. На этом этапе мы выясним, какая самая большая цифра среди всех цифр в сотнях мест. Для этого мы используем тот же трюк с одинарным счетом, за исключением того, что на этот раз указатель называется r, а указатель r2 отмечает его начальную позицию, в которую нам нужно сбрасывать его каждый раз, когда мы переходим к следующему числу.
Начнем с первого номера. Мы начинаем с установки p в 11 (жестко заданная начальная позиция всех чисел). Затем мы используем цикл while, чтобы найти конец числа и устанавливаем p2, чтобы обозначить позицию. В то же время мы также устанавливаем q2 в 0:
Не отвлекайся на то, что q2 отвлекайтесь указывает на переменную. У нас там нет заполнения пустой ячейки, потому что мы можем обнаружить ячейку № 0 просто потому, что она равна нулю.
Далее, мы проходим текущее число, уменьшая p и q2 вместе, пока * p не станет равным нулю. В каждом месте значение * q2 говорит нам, что нам нужно делать. 1 означает «ничего не делать», поэтому мы продолжаем. В конце концов мы сталкиваемся с 2 в ячейке # −3. Каждый раз, когда * q2 не равно 1, q2 всегда равно q .
Как я уже говорил, этап 2 - «определить самую большую цифру в этом месте». Таким образом, мы устанавливаем r в r2 , используем цикл while для уменьшения * p и перемещаем r влево и заполняем ленту 1с, а затем используем другой цикл while для перемещения r назад вправо и увеличиваем * p еще раз, чтобы восстановить значение. Помните, что каждый цикл while выполняется на одну итерацию меньше, чем значение, на котором мы его используем; из-за этого число записанных единиц будет на 3 больше (а не на 4) больше, чем значение цифры, а окончательное значение, сохраненное в * p, будет на 2 больше. Таким образом, это эффективно уменьшилось * p на 2.
После этого мы устанавливаем p в значение p2, а затем делаем все это снова. Во второй раз установите q2 в 0, найдите конец числа, сдвинув p вправо, а затем пройдитесь по цифрам этого числа, уменьшив p и q2 вместе. Еще раз мы встретим 2 в ячейке # −3 и напишем, что много 1s осталось от * r .
В случае третьего числа мы ничего не делаем, потому что у него нет места в сотне (поэтому q2 никогда не достигает q ), но это нормально, потому что это не влияет на вычисление максимального значения цифры.
Мы также устанавливаем ячейку * (г - 4) , которую я здесь пометил немаркированной стрелкой, равной 1 (даже если она уже равна 1). Я не собираюсь говорить вам, почему, но, может быть, вы уже догадались?
Следующее увеличение * q приводит нас к этапу 3, который «вычитает максимальную цифру из всех цифр в текущем месте». Как и прежде, мы сбрасываем p на 11 и q2 на 0, а затем просматриваем все числа, как мы делали на предыдущем этапе; за исключением этого времени, * q = 3 вместо 2. Каждый раз, когда q2 встречает q и p находится в сотнях мест, мы используем цикл while для уменьшения * p столько раз, сколько 1s в блоке слева от * r2 (5 в нашем примере) с использованием гв качестве бегущего указателя. Мы фактически уменьшаем его еще раз, чтобы самая большая цифра заканчивалась на -2, по причине, которая станет ясна позже:
После того как мы обработали все числа, мы находимся в конце этапа 3. Здесь мы выполняем две особые вещи.
- Во-первых, мы также вычитаем размер r- блока (плюс 1) из * q , но используя указатель r2 , который оставляет его слева. * q становится отрицательным таким образом. В нашем случае r- блок имеет пять единиц, поэтому * q становится равным -3.
- Во- вторых, мы устанавливаем переменную из в ненулевое значение , чтобы указать , что мы сейчас вступаем выходной каскад. (Технически тот факт, что * q отрицателен, уже указывает на выходной каскад, но это слишком сложно проверить, следовательно, дополнительная переменная.)
Теперь вы понимаете, что мы продолжаем изучать числа, находим текущее место (обозначенное не-1 значением * q ) внутри каждого числа и делаем что-то в зависимости от значения * q . Мы видим, что * q сначала увеличивается до 2 (= вычислить максимальное значение цифры), затем до 3 (вычитать максимальное значение цифры из каждой цифры в этом месте), а затем мы вычитаем его, чтобы сделать его отрицательным. Оттуда он будет продолжать расти, пока не достигнет 1, восстановив, таким образом, значение, которое означает «ничего не делать». В этот момент мы переходим к следующему месту.
Теперь, когда * q отрицательно, мы выводим. * q имеет точно правильное значение, поэтому мы выведем правильное количество строк символов, прежде чем оно достигнет 1; если самая большая цифра - 2, нам нужно вывести 3 строки. Давайте посмотрим, что происходит при каждом значении * q :
- * q = −2:
- Для первого числа * p равно -2, что указывает на то, что нам нужно вывести
'.'
(точка) или ':'
(двоеточие). Мы решаем, что, посмотрев на q : если это -1, мы в одном месте, поэтому выведите a ':'
(который мы вычисляем как '8'
+2), в противном случае a '.'
.
- Для второго числа * p равно −3. Все, что не равно -2, означает, что мы выводим
'|'
(трубу), а затем увеличиваем значение. Таким образом, он достигнет -2 в нужном месте, а затем мы выводим '.'
s / ':'
s для остальной части этой цифры.
- В каждом случае мы также устанавливаем переменную pd в 0, прежде чем мы обработаем число, и устанавливаем pd (= «print») в ненулевое значение, чтобы указать, что мы напечатали символ.
- Для третьего числа обработка не происходит, потому что третье число не имеет места в сотне. В этом случае pd все равно будет 0 после обработки числа, указывая, что нам все еще нужно вывести a
'|'
(но только если out не равен нулю, потому что в противном случае мы все еще находимся на стадии 2 или 3).
- После обработки всех чисел, если out не равен нулю, выведите новую строку. Обратите внимание, что нам нужна переменная out, чтобы мы не выводили символ новой строки на этапе 2 или 3.
- * q = −1: То же, что и раньше, за исключением того, что * p равно -2 для обоих первых двух чисел, поэтому оба выдают a
'.'
(а третье выдает a,'|'
как и раньше).
- * q = 0: когда * q равно 0, это означает «ничего не делать, если мы на месте, иначе вывести строку
'|'
s независимо от * p ». Таким образом, мы получаем отступ между цифрами.
Теперь мы увеличиваем q, чтобы перейти к следующему месту, десятку, и увеличиваем * q там. В начале этапа 2 лента выглядит так:
Затем мы выполняем этап 2, как и раньше. Помните, что это эффективно вычитает 2 из каждой цифры в этом месте, а также оставляет унарное число слева от * r2, указывающее максимальную цифру. Мы оставляем прежний унарный номер в покое и просто продолжаем расширять ленту влево; это будет стоить только лишнего дополнительного кода для «очистки». Когда мы закончим и увеличим * q , в начале этапа 3 лента будет:
На самом деле, это ложь. Помните ранее, где я сказал, что мы установили * (r - 4) в 1, и я не сказал вам, почему? Теперь я расскажу почему. Это для таких случаев, как этот, где наибольшая цифра фактически равна 0, что означает, что все цифры в этом месте равны 0. Установка * (r - 4) , обозначенная стрелкой без метки выше, в 1 увеличивает унарное число на 1, но только в этом особом случае. Таким образом, мы притворяемся, что наибольшая цифра была 1, что означает, что мы выведем еще одну строку.
После этапа 3 (вычесть максимальную цифру из всех цифр в текущем месте), включая дополнительный шаг, который делает * q отрицательным, лента выглядит следующим образом. В прошлый раз самая большая цифра была представлена -2 в блоке * p , но на этот раз они все -3, потому что все они фактически равны нулю, но мы притворяемся, что максимальная цифра была 1.
Теперь давайте посмотрим, что происходит по мере продвижения * q к 1:
- Когда * q = -1, все значения * p равны -3, что означает, что мы выводим
'|'
s и увеличиваем их.
- Когда * q = 0, мы выводим,
'|'
потому что это то, что мы всегда делаем, когда * q = 0, независимо от * p .
Таким образом, мы получаем два ряда труб.
Наконец, мы перемещаем * q на свое место. Это становится интересным, потому что нам нужно вывести ':'
s, если фактическая цифра не равна 1, а '8'
если - 1. Посмотрим, как работает программа. Сначала мы увеличиваем * q, чтобы начать этап 2:
После этапа 2 («рассчитать максимальное значение цифры») у нас остается следующее:
После этапа 3 («вычесть максимальное значение цифры из всех цифр в текущем месте») лента выглядит следующим образом:
Теперь давайте рассмотрим каждую итерацию * q по очереди:
- * q = −2:
- Первое число: уже в -2, поэтому выведите a
':'
(а не a, '.'
потому что q = -1).
- Второе число: в -4, поэтому выведите a
'|'
и увеличьте.
- Третье число: на -3, поэтому выведите a
'|'
. Однако на этот раз вместо инкремента срабатывает особый случай. Только если мы выводим последнее место ( q = −1), и мы находимся во втором последнем ряду для этого ( * q = −2), а цифра фактически равна 1 ( * p = −3) , то вместо того , чтобы его приращение -2, мы устанавливаем его в -1. Другими словами, мы используем -1 как специальное значение, чтобы указать, что на следующей итерации нам нужно будет выводить '8'
вместо ':'
.
- * q = −1:
- Первое число: уже на -2, поэтому выведите a
':'
.
- Второе число: на -3, поэтому выведите a
'|'
. Специальное условие не срабатывает, потому что * q больше не равен -2. Поэтому прирост.
- Третье число: в -1, поэтому выход
'8'
.
- * q = 0: Обычно мы выводим здесь строку заполнения
'|'
s, но в особом случае, когда мы находимся в одном месте ( q = −1), мы пропускаем это.
После этого q увеличивается до 0, и большой цикл while заканчивается.
Теперь вы знаете, как работает вход, как 202,100,1
. Однако есть еще один особый случай, который мы до сих пор не раскрыли. Возможно, вы помните, что в то время как мы обрабатывали последнее место, когда * p было -3, мы установили его на -1 для 1
(вместо увеличения до -2), чтобы следующая итерация выводила '8'
вместо него. Это работает только потому, что у нас есть итерация, в которой * p равно −3, и мы принимаем решение относительно того, увеличивать его или устанавливать на -1. У нас нет такой итерации, если все цифры в одном месте равны 0 или 1. В таком случае все значения * p для 1 будут начинаться с -2; нет возможности решить установить его на -1а не увеличивать его с −3 . Из-за этого в Стадии 3 есть еще одно условие особого случая («вычитать максимальную цифру из каждой цифры в текущем месте»). Я утверждал, что после вычитания максимального значения цифры из каждой цифры (в этот момент максимальная цифра равна -1), мы просто уменьшаем ее еще раз, но на самом деле есть условие, которое выглядит следующим образом:
Если цифра, на которую мы смотрим, равна максимальной цифре в этом месте ( * p = −1), и это место соответствует единице ( q = −1), а максимальная цифра равна 1 ( * (r +) 5) = 0, т. Е. Унарный блок в самом левом углу имеет длину всего 5 ячеек), только тогда мы оставляем * p на -1, чтобы указать, что единственная итерация вывода должна выводить an '8'
. Во всех остальных случаях мы уменьшаем его еще раз.
Выполнено. С Новым Годом!
Изменить 1 (3183 → 3001): немного счастливого нового года в гольф! Мне удалось полностью избавиться от переменных p2 и r2 ! Теперь p мчится взад и вперед, чтобы найти начало и конец чисел, но в коде оно кажется короче. Я также пытался избавиться от q2 , но так не смог сделать код короче.
Я также нашел еще несколько мест, где я мог бы применять типичные нечитаемые трюки в гольфе, такие как повторное использование последнего значения цикла while. Чтобы дать вам пример, а не
while *(++p) { 1 } // just increment p until *p is 0; the 1 is a noop
if (pd) { x } else { y } // where pd is a variable
Я могу сохранить '""""
(сделать первое, затем второе) и '"""
(константа 1), написав это так, как
if (while *(++p) { pd }) { x } else { y }
Конечно, это работает, только если я знаю, что цикл while будет выполняться как минимум одну итерацию, но если это так, его возвращаемое значение будет pd, поэтому я могу использовать его как условие для if.