BrainF ** k, 396 391 байт
>+>>++++[-<++++++++>]->,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-<+[-<+]->>+[-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>[[->+]->>+<<<+[-<+]->]>+[-<->[[->+]->+>>+<<<<+[-<+]->]<+>->+[->+]->>[->+<]>+>++++++++++>>-<<[-<-[>>]<]<->>>+[-<<<+>>>[-<->]<+++++++++>>>+]++++++++[-<++++<++++++>>]<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]>.>.[-]<[-]<<<[->+<]<<+[-<+]>+]>>[-]<<<-<+[-<+]->>+]
Я не мог устоять перед искушением сделать это. По крайней мере, треугольник остроконечной стороной вниз.
Ввод осуществляется в виде строки числовых символов, за которой следует одна новая строка.
Выходные данные будут содержать один завершающий пробел в каждой строке.
Примеры:
$ bf sd.bf
010
0 1 0
1 1
2
$ bf sd.bf
123456
1 2 3 4 5 6
3 5 7 9 1
8 2 6 0
0 8 6
8 4
2
$ bf sd.bf
9245322
9 2 4 5 3 2 2
1 6 9 8 5 4
7 5 7 3 9
2 2 0 2
4 2 2
6 4
0
объяснение
Поскольку объяснить код довольно сложно с функциональной точки зрения, мы можем вместо этого взглянуть на него с точки зрения состояния ленты в разное время. Основная идея заключается в том, что треугольник, который мы выводим, инициализируется как плотно упакованный (для BF, в любом случае) массив, размер которого уменьшается на 1 при каждой итерации цикла. Другая важная мысль заключается в том, что мы используем 255
для обозначения «заполнителя», который мы можем искать на ленте.
инициализация
Это самый простой шаг. В начале программы мы выполняем следующее:
>+>>++++[-<++++++++>]->
Это переводит ленту в следующее состояние (где >N<
указывается расположение указателя на ленте)
[ 0 1 32 255 >0< 0 0 ...]
Первый номер здесь - это «буфер». Мы не собираемся использовать его на долгосрочной основе, но это полезно для упрощения небольших операций и для копирования данных.
Второе число этого количество мест , которые мы будем Выведение в начале каждой строки, начиная после первой строки. В первой строке не будет пробелов.
Третье число - это пробел, который мы выводим.
Четвертое число - это заполнитель 255, так что мы можем относительно легко вернуться к этой позиции.
вход
С этой позиции мы будем читать все символы. В конце этого шага мы надеемся оказаться в следующей ситуации:
[ 0 1 32 255 a b c d e f ... >255< 0 0 ... ]
Где a b c d e f ...
указывает строку числовых символов, которые были введены (не перевод строки).
Мы осуществляем это с помощью следующего:
,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-
В этом есть некоторые нюансы. Прежде всего, мы будем выводить каждый символ по мере его получения, а затем выводить пробел после него. Во-вторых, мы не хотим копировать значение ASCII на ленту, мы хотим скопировать действительную цифровую цифру. В-третьих, мы хотим остановиться, когда выйдем на новую строку, и оставить себя в то время в хорошем месте.
Скажите, что наш вклад 6723
. Затем, после прочтения первого 6
, наша лента выглядит так:
[ 0 1 32 255 >54< 0 0 ...]
Мы проверяем, что это значение не равно 10
(новая строка ASCII) с ,----------[++++++++++
. Затем мы выводим значение и продолжаем, одновременно вычитая 48 из входного значения и добавляя 32 к значению рядом с ним ( >>++++++++[-<++++<------>>]<
), оставляя нас здесь:
[ 0 1 32 255 6 >32< 0 ...]
Обратите внимание, что на протяжении всего этого процесса мы можем предполагать, что все цифры справа от нашего ввода равны 0 - это означает, что мы не рискуем испортить любое предыдущее состояние, если мы будем использовать значения справа для вычисления 6 * 8
и 4 * 8
.
Теперь мы выводим только что сгенерированный символ пробела и вводим новый ввод, удаляя вычисленное там пространство. В конце концов, ввод будет завершен новой строкой, и цикл завершится, оставив там, 255
где была бы новая строка ( ,----------]-
). Это второй символ-заполнитель, который мы будем использовать для навигации по ленте. На данный момент в нашем сценарии наша лента точно такая:
[ 0 1 32 255 6 7 2 3 >255< 0 0 ... ]
расчет
Это работает так, что список цифр между нашими 255
заполнителями будет сокращаться на одну итерацию цикла. Когда в нем осталась только 1 цифра, мы закончили и должны немедленно остановиться (обратите внимание, что на данный момент каждая цифра в этом списке уже выведена, поэтому нам не нужно беспокоиться о ее выводе снова).
Теперь мы используем этот трюк , чтобы перейти к первому 255
заполнителя: <+[-<+]-
. Это эффективно ищет ленту слева 255
, ничего не меняя между ними. Теперь, когда мы переместили указатель, мы можем проверить наше условие выхода: если в списке только одна цифра, то ячейка будет содержать два пробела справа 255
. Таким образом, мы проверяем это и запускаем цикл:>>+[-<<
Первым шагом в нашем цикле является вывод новой строки. Итак, мы переходим к первой ячейке (нашей буферной ячейке), добавляем к ней 10 и выводим. Следующим шагом является вывод всех начальных пробелов. После их вывода мы увеличиваем наш счетчик на число ведущих пробелов. Эти шаги выполняются следующим образом:
-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>
Что оставляет нас в таком состоянии:
[ 0 2 32 255 >6< 7 2 3 255 0 0 0 0 0 0 ]
Наш следующий шаг - скопировать первое значение в списке после второго заполнителя 255
:
[[->+]->>+<<<+[-<+]->]
По сути, мы делаем это, прыгая туда-сюда между нашими заполнителями 255
, оставляя нас здесь:
[ 0 2 32 255 >0< 7 2 3 255 0 6 0 0 ... ]
Теперь мы начинаем цикл, перебирая остальную часть списка, останавливаясь, когда мы нажимаем 255
:>+[-<
На этом этапе цифра слева от нас всегда равна 0. Поэтому, поскольку мы любим их, мы вставляем 255
туда заполнитель, чтобы мы могли вернуться к нашему месту в списке. Следующий шаг - переместить второе место в списке в те места, где мы переместились на первое место, после второго заполнителя 255
. Эти шаги выполняются следующим образом:
->
[[->+]->+>>+<<<<+[-<+]->]
Оставив нас здесь: [ 0 2 32 255 255 >0< 2 3 255 7 6 7 0 ]
Теперь, и 6
и 7
были перемещены в место, где могут произойти вычисления. Нам нужны две копии, 7
потому что это понадобится и следующему номеру в списке. 7
Сразу же после 255
этой цели служит, тогда как другой 7
будет потребляться расчета.
Сначала добавим две цифры:
<+>->+[->+]->>
[->+<]>
Оставив нас здесь:
[ 0 2 32 255 0 255 2 3 255 7 0 >13< 0 ]
Следующая комбинация шагов является самой сложной. Нам нужно посмотреть, больше ли число, на которое мы указываем, больше 10, и если это так, мы вычтем 10
. На самом деле мы вычитаем из него 10 и видим, попадет ли он 0
в какой-либо момент вычитания. Если это произойдет, мы добавим 10
позже. В конце этого у нас должна быть сумма по модулю 10.
Prepare a 10 to the right
+>++++++++++
Leave yet another 255 for a loop condition later
>>-<<
If the number is greater than 10 end up one space to the left
else one space to the right
[-<-[>>]<]<->
Check if the previous 255 is two spaces to the right and if it is
add 10 back to our sum--we've subtracted too much
>>+[-<<<+>>>[-<->]<+++++++++>>>+]
На данный момент мы достигли цели. У нас есть сумма по модулю 10! Кроме того, было ли число больше 10, мы в конечном итоге здесь:
[ 0 2 32 255 0 255 2 3 255 7 0 3 0 0 >0< ]
Наши следующие цели - вывести эту новую сумму, добавить за ней пробел и вставить ее обратно в наш список. Мы делаем все это с помощью наших предыдущих приемов 255
прыжка и добавления 48
к нашей сумме, поэтому я не буду подробно останавливаться на этом.
++++++++[-<++++<++++++>>]
<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]
>.>.
И мы здесь: [ 0 2 32 255 3 255 2 3 255 7 0 0 51 >32< ]
обратите внимание, как мы помещаем дополнительный 255
заполнитель после нашей новой инъекции, 3
чтобы не потерять место в списке. На этом этапе мы вывели нашу сумму и ее пространство, поэтому нам нужно очистить и вернуться в состояние, в котором будет работать следующая итерация этого цикла. Нам нужно очистить ячейки 51
и 32
ячейки, переместить 7
один раз вправо и перейти к заполнителю нашего списка, чтобы мы могли начать все сначала.
[-]<[-]<<<[->+<]<<+[-<+]
Теперь мы здесь: [ 0 2 32 255 3 >0< 2 3 255 0 7 0 ... ]
это именно то место, где мы хотим быть для нашей следующей итерации. Так что проверяйте на 255 и двигайтесь дальше! ( >+]
)
Когда мы выпадем из цикла, у нас будет целый новый список, состоящий из сумм из предыдущего списка. В первый раз это будет выглядеть так:
[ 0 2 32 255 3 9 5 0 >0< ]
Теперь мы хотим повторить весь этот процесс в нашем новом списке, поэтому мы швыряемся 255
вниз влево и начинаем все сначала! Нам нужно сделать небольшую очистку >>[-]<<
, а затем оставить наш заполнитель <-
. После этого мы находимся точно в том же месте, что и после ввода, поэтому мы можем выполнить те же проверки:<+[-<+]->>+
и бум! У нас есть полный цикл! Все , что нам нужно это закрывающая скобка, и когда он заканчивается , мы уже на выходе все, поэтому мы сделали: ]
.