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
.
Тепер ми виводимо символ простору, який ми щойно створили, і вводимо новий ввід, видаляючи прорахований нами простір. Врешті-решт, вхід буде припинено новим рядком, і цикл вийде, залишаючи a, 255
де був би новий рядок ( ,----------]-
). Це другий символ заповнювача, який ми будемо використовувати для навігації по стрічці. На даний момент у нашому сценарії наша стрічка саме така:
[ 0 1 32 255 6 7 2 3 >255< 0 0 ... ]
Розрахунок
Як це працює, це те, що список цифр між нашими 255
заповнювачами зменшиться по одній ітерації циклу. Коли в ньому залишилося лише одна цифра, ми закінчили і повинні негайно зупинитися (зауважте, що в цей момент кожна цифра у цьому списку вже виведена, тому нам не доведеться турбуватися про її виведення знову).
Тепер ми використовуємо цей трюк , щоб перейти до першого 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
униз ліворуч і починаємо все спочатку! Нам потрібно зробити трохи очищення >>[-]<<
, а потім скинути наш заповнювач <-
. Після цього ми знаходимося в тому самому місці, що і після введення, тому ми можемо піти, роблячи ті самі перевірки: <+[-<+]->>+
і бум! У нас є наш повний цикл! Все , що нам потрібно це закриває дужка, і коли він закінчується , ми вже на виході все, тому ми зробили: ]
.