Mini-Flak, 6851113 циклів
Програма (буквально)
Я знаю, що більшість людей, ймовірно, не очікують, що у Quine Mini-Flak використовується недруковані символи та навіть багатобайтові символи (що робить кодування релевантним). Тим не менш, ця quine робить, і недруковані файли, у поєднанні з розміром quine (93919 символів, закодованих як 102646 байт UTF-8), ускладнюють розміщення програми на цій посаді.
Однак програма дуже повторювана, і як така, стискає дійсно добре. Таким чином, що вся програма доступна буквально з Stack Exchange, є xxd
зворотний шестигранник gzip
компресованої версії повної quine, прихованої за розбірним нижче:
00000000: 1f8b 0808 bea3 045c 0203 7175 696e 652e .......\..quine.
00000010: 6d69 6e69 666c 616b 00ed d9db 6a13 4118 miniflak....j.A.
00000020: 0060 2f8b f808 0d64 a1c1 1dc8 4202 c973 .`/....d....B..s
00000030: 4829 4524 0409 22e2 5529 a194 1242 1129 H)E$..".U)...B.)
00000040: d2d7 ca93 f9cf 4c4c d45b 9536 e6db 6967 ......LL.[.6..ig
00000050: 770e 3bc9 ffed eca9 edb7 b1a4 9ad2 6a1d w.;...........j.
00000060: bfab 75db c6c6 6c5f 3d4f a5a6 8da6 dcd8 ..u...l_=O......
00000070: 465b d4a5 5a28 4bd9 719d 727b aa79 f9c9 F[..Z(K.q.r{.y..
00000080: 43b6 b9d7 8b17 cd45 7f79 d3f4 fb65 7519 C......E.y...eu.
00000090: 59ac 9a65 bfdf 8f86 e6b2 69a2 bc5c 4675 Y..e......i..\Fu
000000a0: d4e4 bcd9 5637 17b9 7099 9b73 7dd3 fcb2 ....V7..p..s}...
000000b0: 4773 b9bc e9bd b9ba 3eed 9df7 aeaf 229d Gs......>.....".
000000c0: e6ed 5eae 3aef 9d46 21b2 5e4d bd28 942e ..^.:..F!.^M.(..
000000d0: 6917 d71f a6bf 348c 819f 6260 dfd9 77fe i.....4...b`..w.
000000e0: df86 3e84 74e4 e19b b70e 9af0 111c fa0d ..>.t...........
000000f0: d29c 75ab 21e3 71d7 77f6 9d8f f902 6db2 ..u.!.q.w.....m.
00000100: b8e1 0adf e9e0 9009 1f81 f011 18d8 1b33 ...............3
00000110: 72af 762e aac2 4760 6003 1bd8 698c c043 r.v...G``...i..C
00000120: 8879 6bde 9245 207c 04ae 5ce6 2d02 e1bb .yk..E |..\.-...
00000130: 7291 4540 57f8 fe0d 6546 f89b a70b 8da9 r.E@W...eF......
00000140: f5e7 03ff 8b8f 3ad6 a367 d60b f980 679d ......:..g....g.
00000150: d3d6 1c16 f2ff a767 e608 57c8 c27d c697 .......g..W..}..
00000160: 4207 c140 9e47 9d57 2e50 6e8e c215 b270 B..@.G.W.Pn....p
00000170: bdf6 9926 9e47 9d05 ce02 0ff0 5ea7 109a ...&.G......^...
00000180: 8ba6 b5db 880b 970b 9749 2864 47d8 1b92 .........I(dG...
00000190: 39e7 9aec 8f0e 9e93 117a 6773 b710 ae53 9........zgs...S
000001a0: cd01 17ee b30e d9c1 15e6 6186 7a5c dc26 ..........a.z\.&
000001b0: 9750 1d51 610a d594 10ea f3be 4b7a 2c37 .P.Qa.......Kz,7
000001c0: 2f85 7a14 8fc4 a696 304d 4bdf c143 8db3 /.z.....0MK..C..
000001d0: d785 8a96 3085 2acc 274a a358 c635 8d37 ....0.*.'J.X.5.7
000001e0: 5f37 0f25 8ff5 6854 4a1f f6ad 1fc7 dbba _7.%..hTJ.......
000001f0: 51ed 517b 8da2 4b34 8d77 e5b2 ec46 7a18 Q.Q{..K4.w...Fz.
00000200: ffe8 3ade 6fed b2f2 99a3 bae3 c949 9ab5 ..:.o........I..
00000210: ab75 d897 d53c b258 a555 1b07 63d6 a679 .u...<.X.U..c..y
00000220: 4a51 5ead a23a 6a72 9eb6 d569 960b f3dc JQ^..:jr...i....
00000230: 9ceb 53fa 658f 345f ad07 6f6f efce 06ef ..S.e.4_..oo....
00000240: 0677 b791 cef2 f620 57bd 1b9c 4521 b241 .w..... W...E!.A
00000250: 4d83 2894 2eaf a140 8102 050a 1428 50a0 M.(....@.....(P.
00000260: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000270: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000280: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000290: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
000002a0: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
000002b0: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
000002c0: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
000002d0: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
000002e0: 0205 0a14 2850 a040 8102 050a 1428 50a0 ....(P.@.....(P.
000002f0: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000300: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000310: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000320: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
00000330: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
00000340: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
00000350: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
00000360: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
00000370: 0205 0a14 2850 a01c 14ca 7012 cbb4 a6e9 ....(P....p.....
00000380: e6db e6b1 e4b1 9e4c 4ae9 d3be f5f3 745b .......LJ.....t[
00000390: 37a9 3d6a af49 7489 a6e9 ae5c 96dd 488f 7.=j.It....\..H.
000003a0: d31f 5da7 fbad 5d56 3e73 5277 7cf5 aa7b ..]...]V>sRw|..{
000003b0: 3fbc df7c e986 c3ba 5ee4 3c6f 74f7 c3e1 ?..|....^.<ot...
000003c0: 301a bb45 d795 9afb fbdc 1495 65d5 6d9b 0..E........e.m.
000003d0: baf7 a5b4 a87d 4a5b d7fd b667 b788 ec27 .....}J[...g...'
000003e0: c5d8 28bc b96a 9eda 7a50 524d 290a a5cb ..(..j..zPRM)...
000003f0: cbef 38cb c3ad f690 0100 ..8.......
(Так, це настільки повторюється, що ви навіть можете побачити повтори після його стискання).
Питання говорить: "Я також дуже рекомендую не запускати свою програму в TIO. Мало того, що TIO повільніше, ніж настільний інтерпретатор, але також буде таймаут приблизно через хвилину. Було б надзвичайно вражаюче, якби комусь вдалося набрати достатньо низький рівень для запуску їх програма до вичерпання часу TIO. " Я можу зробити це! На TIO потрібно близько 20 секунд, використовуючи інтерпретатор Ruby: Спробуйте в Інтернеті!
Програма (читається)
Тепер я дав версію програми, яку можуть читати комп’ютери, давайте спробуємо версію, яку можуть прочитати люди. Я перетворив байти, що складають квину, у кодову сторінку 437 (якщо вони мають високий набір бітів) або контрольні зображення Unicode (якщо вони керують кодами ASCII), додав пробіл (будь-який попередній пробіл був перетворений на контрольні зображення ), кодується за допомогою довжини за допомогою синтаксису «string×length»
, а деякі біти, важкі для даних:
␠
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
… much more data encoded the same way …
(«()×117»(«()×115»(«()×117»
«000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
("Практично 241" - це те, що в 241-й копії відсутня остання '
, але в іншому випадку ідентична іншим 240.)
Пояснення
Про коментарі
Перше, що потрібно пояснити, що з недрукованими символами та іншими мотлохами, що не є командами Mini-Flak? Ви можете подумати, що додавання коментарів до quine просто ускладнює справи, але це конкуренція за швидкість (а не конкуренція за розмірами), тобто коментарі не шкодять швидкості програми. Тим часом Brain-Flak, а отже, і Mini-Flak, просто скидають вміст стеку до стандартного виводу; якби вам довелося переконатися, що стек містив тількисимволи, які складали команди вашої програми, вам доведеться проводити цикли, чистячи стек. Наразі, Brain-Flak ігнорує більшість символів, тому якщо ми гарантуємо, що елементи стек-файлів не є дійсними командами Brain-Flak (що робить це поліглотом Brain-Flak / Mini-Flak) і не є негативними або зовні Діапазон Unicode, ми можемо просто залишити їх у стеку, дозволити їх виведення та поставити той самий символ у нашій програмі на те саме місце, щоб зберегти властивість quine.
Є один особливо важливий спосіб, яким ми можемо скористатися цим. Quine працює за допомогою довгих рядків даних, і в основному весь вихід з quine виробляється шляхом форматування рядка даних різними способами. Існує лише один рядок даних, незважаючи на те, що програма має кілька фрагментів; тому нам потрібно мати можливість використовувати один і той же рядок даних для друку різних частин програми. Трюк "непотрібні дані не мають значення" дозволяє нам це зробити дуже простим способом; ми зберігаємо символи, що складають програму, у рядок даних, додаючи або віднімаючи значення до коду ASCII або з нього. Зокрема, символи, що становлять початок програми, зберігаються як їх ASCII код + 4, символи, що складають розділ, який повторюється майже 241 раз як їх ASCII код - 4,кожен символ рядка даних зі зміщенням; якщо, наприклад, ми друкуємо його з 4-ма доданими до кожного коду символів, ми отримуємо одне повторення повторного розділу з деякими коментарями до і після. (Ці коментарі - це просто інші розділи програми. Коди символів зміщені так, що вони не утворюють дійсних команд Brain-Flak, оскільки було додано неправильне зміщення. Ми повинні ухилятися від команд Brain-Flak, а не лише Mini- Команди Flak, щоб уникнути порушення частини запитання з обмеженим джерелом ; вибір зсувів був покликаний забезпечити це.)
Через цей фокус з коментарями нам насправді потрібно лише вміти виводити рядок даних, відформатований двома різними способами: а) кодується так само, як у джерелі; б) як символьні коди із заданим зміщенням, що додаються до кожного коду. Це величезне спрощення, що робить додаткову довжину цілком вартим.
Структура програми
Ця програма складається з чотирьох частин: вступ, рядок даних, формат даних рядків даних та вихідний текст. Вступ та outro в основному відповідають за запуск рядка даних та його форматера в циклі, щоразу вказуючи відповідний формат (тобто, кодувати чи зсув, і який зсув використовувати). Рядок даних - це лише дані, і є єдиною частиною ланцюжка, для якої символи, що складають її, не вказуються буквально в рядку даних (робити це, очевидно, було б неможливо, оскільки це повинно бути довше себе); Таким чином, це написано таким чином, що особливо легко відроджувати з себе. Форматор рядка даних складається з 241 майже однакових частин, кожна з яких формує конкретну дату з 241 в рядку даних.
Кожна частина програми може бути створена за допомогою рядка даних та її форматера наступним чином:
- Щоб створити вихід, відформатуйте рядок даних зі зміщенням +8
- Щоб створити формат даних рядка даних, відформатуйте рядок даних зі зміщенням +4, 241 раз
- Щоб створити рядок даних, відформатуйте рядок даних, кодуючи її у вихідний формат
- Щоб створити вступ, відформатуйте рядок даних із зміщенням -4
Тому все, що нам потрібно зробити, - це подивитися, як працюють ці частини програми.
Рядок даних
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …
Нам потрібно просте кодування для рядка даних, оскільки ми повинні мати змогу змінити кодування в коді Mini-Flak. Ви не можете стати набагато простішим за це!
Ключова ідея цієї квоти (крім трюку коментарів) полягає в тому, що в основному є лише одне місце, де ми можемо зберігати великі обсяги даних: "суми повернених значень команд" в межах різних рівнів гніздування джерела програми. (Це зазвичай називається третім стеком, хоча у Mini-Flak немає другого стека, тому "робочий стек", ймовірно, краще ім'я в контексті Mini-Flak.) Іншими можливостями для зберігання даних буде головний / перший стек (який не працює тому що саме там повинен проходити наш вихід, і ми не можемо віддалено виводити повз сховище віддалено ефективним способом) і кодується в бінду в одному елементі стека (що не підходить для цієї проблеми, оскільки це потребує експоненціального часу для витягнути з нього дані); коли ви їх усунете, робочий стек - єдине місце, що залишилося.
Для того, щоб "зберігати" дані в цьому стеку, ми використовуємо незбалансовані команди (в цьому випадку перша половина (…)
команди), які згодом будуть врівноважені у форматуванні рядків даних. Кожен раз, коли ми закриваємо одну з цих команд у форматі, вона підштовхує суму даної даних, взятої з рядка даних, і повертає значення всіх команд на цьому рівні вкладення у форматері; ми можемо гарантувати, що останні додають до нуля, тому форматник просто бачить окремі значення, взяті з рядка даних.
Формат дуже простий: з (
наступними n копіями ()
, де n - номер, який ми хочемо зберегти. (Зверніть увагу, що це означає, що ми можемо зберігати лише негативні числа, і останній елемент рядка даних повинен бути позитивним.)
Одне злегка неінтуїтивних моментів щодо рядка даних полягає в тому, в якому порядку він знаходиться. "Старт" рядка даних - це кінець, що знаходиться ближче до початку програми, тобто самий зовнішній рівень вкладення; ця частина набуває останнього форматування (оскільки формат працює з найглибшого до зовнішнього рівня гніздування). Тим НЕ менше, незважаючи на те , відформатована в минулому, він отримує надрукований перший, оскільки значення в стек першим роздруковуються останнім інтерпретатором Mini-Flak. Цей же принцип застосовується і до програми в цілому; нам потрібно спершу відформатувати outro, потім формат даних рядка даних, потім рядок даних, потім вступ, тобто зворотній порядок, в якому вони зберігаються в програмі.
Форматор рядка даних
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
Форматор рядка даних складається з 241 розділів, кожен з яких має однаковий код (один розділ має незначно різний коментар), кожен з яких формує один конкретний символ рядка даних. (Тут ми не могли використати цикл: нам потрібен неврівноважений, )
щоб прочитати рядок даних шляхом відповідності його незбалансованому (
, і ми не можемо поставити один із них всередині {…}
циклу, єдина форма циклу, яка існує. Отже, ми " розгорніть "форматер і просто отримайте вступ / вивід для виведення рядка даних із зміщенням форматера 241 разів.)
)[({})( … )]{}
Найбільш зовнішня частина елемента форматера читає один елемент рядка даних; простота кодування рядка даних призводить до невеликої складності в його читанні. Ми починаємо із закриття незрівняного (…)
в рядку даних, потім заперечуємо ( […]
) два значення: дату, яку ми щойно прочитали з рядка даних ( ({})
), і повернене значення решти програми. Ми копіюємо повернене значення решти елемента форматера з (…)
та додаємо копію до заперечної версії {}
. Кінцевим результатом є те, що значення, що повертається елементом рядка даних та елементом форматера разом, є даним мінус мінус значення мінус значення повернення плюс значення повернення або 0; це необхідно для того, щоб наступний елемент рядка даних видав правильне значення.
([({})]({}{}))
Форматор використовує елемент верхнього стека, щоб знати, в якому режимі він перебуває (0 = формат у форматуванні рядків даних, будь-яке інше значення = зміщення для виведення). Однак, лише прочитавши рядок даних, дата знаходиться на вершині формату на стеку, і ми хочемо їх навпаки. Цей код є більш коротким варіантом коду підкачки Brain-Flak, який має вище b до b над a + b ; він не тільки коротший, але й корисніший (у цьому конкретному випадку), оскільки побічний ефект додавання b до a не є проблематичним, коли b дорівнює 0, а коли b - не 0, це робить для нас розрахунок зміщення.
{
((()[()]))
}{}
{
…
((()[()]))
}{}
Brain-Flak має лише одну структуру потоку управління, тому, якщо ми хочемо нічого, крім while
циклу, це займе трохи роботи. Це структура "заперечення"; якщо на вершині стека є 0, він видаляє його, інакше він ставить 0 вгорі стека. (Це працює досить просто: доки на вершині стеку немає 0, натисніть 1 - 1 на стек двічі; коли ви закінчите, викладіть верхній елемент стека.)
Можна розмістити код всередині заперечної структури, як показано тут. Код запускається лише в тому випадку, якщо верхня частина стека була ненульовою; тому якщо у нас є дві заперечні структури, якщо припустити, що два верхніх елемента стека не є обома нульовими, вони скасують один одного, але будь-який код всередині першої структури запуститься лише в тому випадку, якщо верхній елемент стека був ненульовим, а код всередині друга структура працюватиме лише в тому випадку, якщо верхній елемент стека дорівнював нулю. Іншими словами, це еквівалент твердження if-then-else.
У пункті "тоді", яке запускається, якщо формат ненульовий, ми насправді не маємо нічого робити; що ми хочемо - це підсунути дані + зміщення до основного стеку (щоб він міг виводитися в кінці програми), але це вже є. Тож нам залишається мати справу лише з випадком кодування елемента рядка даних у вихідному вигляді.
{
({}(((({}())[()])))[{}()])
}{}
(({}))
Ось як ми це робимо. {({}( … )[{}()])}{}
Структура повинна бути знайома як цикл з певним числом ітерацій (який працює шляхом переміщення лічильника циклу до робочої стеку і утримуючи її там, вона буде в безпеці від будь-якого іншого коду, так як доступ до робочої стеці прив'язаний до рівень гніздування програми). Тіло циклу є ((({}())[()]))
, що робить три копії верхнього елемента стека і додає 1 до нижнього. Іншими словами, він перетворює 40 на вершині стека в 40 вище 40 вище 41, або розглядається як ASCII, (
в (()
; працює цей раз буде робити (
в (()
в (()()
в (()()()
і так далі, і , таким чином , являє собою простий спосіб для створення нашої рядки даних (за умови , що є (
на вершині стека вже).
Як тільки ми закінчимо цикл, (({}))
дублює верхню частину стека (так що тепер вона починається, ((()…
а не (()…
. Ведуча (
буде використана наступною копією форматера рядка даних для форматування наступного символу (він розширить його на (()(()…
потім (()()(()…
, і так далі, тому це генерує розділення (
в рядку даних).
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'
Існує ще один останній інтерес у форматуванні рядків даних. Гаразд, тому в основному це просто перенесений 4 кодові точки вниз; однак цей апостроф наприкінці може виглядати поза місцем. '
(кодова точка 39) перетвориться на +
(кодова точка 43), що не є командою Brain-Flak, тому ви, можливо, здогадалися, що вона існує для якоїсь іншої мети.
Причина тут полягає в тому, що формат даних рядків очікує, що там вже буде (
стек (він ніде не містить буквальних 40). The'
насправді на початку блоку, який повторюється, щоб скласти формат даних рядків даних, а не в кінці, тому після того, як символи форматора рядка даних будуть висунуті на стек (і код ось-ось перейде до друку рядка даних Сам по собі outro перетворює 39 на вершині стека в 40, готовий для того, щоб форматер (сам запущений формат цього разу, а не його представлення в джерелі) використовувати його. Ось чому у нас є "майже 241" копії форматера; у першій копії відсутній її перший символ. І цей персонаж, апостроф, є одним із лише трьох символів у рядку даних, які не відповідають коду Mini-Flak десь у програмі; це там суто як метод забезпечення константи.
Вступ та аутро
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
…
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
Вступ і вивід є концептуальною частиною програми; Єдина причина, яку ми вирізняємо, полягає в тому, що outro потрібно виводити перед тим, як рядок даних та його форматер (щоб він друкував після них), тоді як вступ потрібно виводити після них (друкуючи перед ними).
(((()()()()){}))
Почнемо, помістивши дві копії з 8 на стек. Це зсув для першої ітерації. Друга копія полягає в тому, що основний цикл очікує, що над версією стека буде елемент небажаної зсуву, залишений від тесту, який вирішує, чи існує головна петля, і тому нам потрібно помістити туди непотрібний елемент, щоб це не викидає елемент, якого ми насправді хочемо; копія - це найшвидший (таким чином, найшвидший спосіб отримання) спосіб зробити це.
Є й інші подання числа 8, які не довше цього. Однак, коли йдеться про найшвидший код, це, безумовно, найкращий варіант. По-перше, використання ()()()()
швидше, ніж, скажімо, (()()){}
тому що, незважаючи на те, що обидва є 8 символами, перший - цикл швидше, тому що (…)
рахується як 2 цикли, але ()
лише як один. Збереження одного циклу є мізерним порівняно з набагато більшим врахуванням для quine : (
і )
мати набагато менші кодові точки, ніж {
та }
, тому генерування для них фрагменту даних буде набагато швидше (і фрагмент даних займе менше місця в коді, теж).
{{} … }{}{}
Основна петля. Це не враховує ітерацій (це while
цикл, а не for
цикл, і для тестування використовується тест). Як тільки він виходить, ми відкидаємо два верхні елементи стека; верхній елемент - нешкідливий 0, але внизу елемент буде "форматом, який слід використовувати при наступній ітерації", який (будучи негативним зміщенням) - це від'ємне число, і якщо на стеку є якісь негативні цифри, коли Mini -Flak програма виходить, інтерпретатор виходить з ладу, намагаючись вивести їх.
Оскільки цей цикл використовує явний тест, щоб вийти з нього, результат цього тесту залишиться у стеку, тому ми відкидаємо його як перше, що ми робимо (його значення не корисне).
(({})[(()()()())])
Цей код натискає 4 і f - 4 над елементом стеки f , залишаючи цей елемент на місці. Ми обчислюємо формат для наступної ітерації заздалегідь (хоча у нас є постійні 4 зручні) і одночасно отримуємо стек у правильному порядку для наступних кількох частин програми: ми будемо використовувати f як формат для ця ітерація, і 4 потрібні перед цим.
(({})( … )[{}])
Це зберігає копію f - 4 на робочому стеку, щоб ми могли використовувати її для наступної ітерації. (Значення f все ще буде присутнє в цій точці, але воно буде в незручному місці на стеку, і навіть якщо ми зможемо маневрувати до потрібного місця, нам доведеться провести цикли, віднімаючи від нього 4, і цикли друку коду, щоб зробити це віднімання. Набагато простіше просто зараз зберігати його.)
{{}{}((()[()]))}{}
Тест на перевірку того, чи є зміщення 4 (тобто f - 4 - 0). Якщо це так, ми друкуємо формат даних рядків даних, тому нам потрібно запустити рядок даних та його форматтер 241 раз, а не один раз при цьому зміщення. Код досить простий: якщо f - 4 ненульовий, замініть f - 4 і сам 4 парою нулів; то в будь-якому випадку вставте верхній елемент стека. Тепер у нас на стеці є число вище f , або 4 (якщо ми хочемо надрукувати цю ітерацію 241 раз), або 0 (якщо ми хочемо надрукувати його лише один раз).
(
((((((({})){}){}{})){}{}){}){}
()
)
Це цікавий вид константи Brain-Flak / Mini-Flak; довгий рядок тут позначає число 60. Вас можуть збентежити відсутність ()
, які зазвичай є константами Брейн-Флак; це не звичайне число, а церковна цифра, яка інтерпретує числа як операцію дублювання. Наприклад, церковна цифра 60, яку можна побачити тут, робить 60 примірників своїх даних і об'єднує їх усі разом в одне значення; у Brain-Flak єдине, що ми можемо поєднати - це звичайні цифри, до того ж ми додаємо 60 копій верхньої частини стека і таким чином множимо верхню частину стека на 60.
Як бічну примітку, ви можете використовувати шукач чисел Underload , який генерує церковні цифри в синтаксисі Underload, щоб знайти відповідне число і в Mini-Flak. Число недовантаження (крім нуля) використовує операції "дублювати верхній елемент стека" :
та "поєднувати два верхні елементи стека" *
; обидві ці операції існують у Brain-Flak, тому ви просто перекладете :
на )
, *
до {}
, додайте а {}
та додайте достатньо (
на початку для балансування (для цього використовується дивна суміш основного стека та робочого стека, але це працює).
Цей конкретний фрагмент коду використовує церковну цифру 60 (фактично фрагмент "помножити на 60") разом із збільшенням, щоб генерувати вираз 60 x + 1. Отже, якщо у нас було 4 з попереднього кроку, це дає нам значення 241, або якщо у нас було 0, ми просто отримуємо значення 1, тобто це правильно обчислює кількість необхідних нам ітерацій.
Вибір 241 не випадковий; це значення було обрано таким чином: а) приблизно довжина, при якій програма все-таки закінчиться, і b) 1 більше, ніж 4 рази кругле число. Круглі цифри, 60 у цьому випадку, як правило, мають коротші зображення як церковні цифри, оскільки у вас є більш гнучкість факторів для копіювання. Програма згодом містить прокладки, щоб точно довести довжину до 241.
{
({}(
…
)[{}()])
}{}
Це цикл for, як той, який бачили раніше, який просто запускає код всередині нього кілька разів, рівний верхній частині основного стека (який він споживає; сам лічильник циклу зберігається на робочому стеку, але видимість що прив'язане до рівня гніздування програми, і тому неможливо нічого, крім циклу, взаємодіяти з нею). Це фактично запускає рядок даних та його форматтер 1 або 241 раз, і оскільки ми вискакували всі значення, які ми використовували для нашого обчислення потоку управління з основного стеку, ми маємо формат, який слід використовувати поверх нього, готовий до форматер, який потрібно використовувати.
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
Коментар тут не зовсім без інтересу. Для одного - є кілька команд Brain-Flak; )
в кінці природно генеруються в якості побічного ефекту шляху переходи між різними сегментами роботи програми, так що (
на початку були вручну додані , щоб збалансувати його (і не дивлячись на довжину коментар зсередини, поставивши коментар всередині ()
команда по - , як і раніше ()
команда, так що все це робить додавання 1 до поверненню значенню рядка даних і її форматіровщіку що - то , що цикл повністю ігнорує).
Більше того, що ці символи NUL на початку коментаря явно не компенсуються ні з чого (навіть різниці між +8 і -4 недостатньо, щоб перетворити (
нуль на NUL). Це чиста підкладка, щоб довести рядок даних 239 елементів до 241 елемента (що легко платить за себе: знадобиться набагато більше двох байтів, щоб генерувати 1 проти 239, а не 1 проти 241 при розрахунку кількості необхідних ітерацій ). NUL був використаний як символ прокладки, оскільки він має найменшу можливу кодову точку (робить вихідний код для рядка даних коротшим і тим самим швидшим для виведення).
{}({}())
Відкиньте верхній елемент стека (формат, який ми використовуємо), додайте 1 до наступного (останній символ, який потрібно вивести, тобто перший символ, який слід надрукувати, в розділі програми, який ми щойно відформатували). Старий формат нам більше не потрібен (новий формат ховається на робочому стеку); і приріст у більшості випадків є нешкідливим і змінює '
на одному кінці вихідного подання форматник рядків даних на формат (
(який необхідний на стеці для наступного запуску форматера, щоб відформатувати сам рядок даних). Нам потрібна така трансформація в outro або intro, тому що змушення кожного елемента форматування рядка даних починати з (
нього зробить його дещо складнішим (оскільки нам потрібно буде закрити, (
а потім скасувати його ефект згодом), інам би хотілося (
десь генерувати додаткові, оскільки у нас є майже 241 копії форматера, а не всі 241 (тож найкраще, щоб такий нешкідливий персонаж '
був таким, якого не вистачає).
(({})(()()()()){})
Нарешті, тест виходу з циклу. Поточна вершина основного стека - це потрібний нам формат для наступної ітерації (щойно повернувся з робочого стеку). Це копіює його і додає 8 до копії; отримане значення буде відкинуто наступного разу навколо циклу. Однак якщо ми щойно надрукували вступ, зміщення склало -4, так що зміщення для "наступної ітерації" складе -8; -8 + 8 дорівнює 0, тому цикл вийде, а не продовжить ітерацію після цього.