Мини-Флак, 6851113 циклов
Программа (буквально)
Я знаю, что большинство людей вряд ли ожидают, что в мини-Flak-квине будут использоваться непечатаемые символы и даже многобайтовые символы (что делает кодировку актуальной). Тем не менее, эта квинна делает, и непечатаемые, в сочетании с размером квинс (93919 символов, закодированных как 102646 байт UTF-8), довольно затрудняют размещение программы в этом посте.
Тем не менее, программа очень повторяющаяся и, как таковая, сжимается очень хорошо. Так что вся программа доступна буквально из Stack Exchange, xxd
за gzip
разворачиваемой таблицей скрыта шестнадцатеричная двусторонняя шестнадцатеричная версия полной сжатой версии:
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 на интерпретаторе Ruby занимает около 20 секунд: попробуйте онлайн!
Программа (читабельно)
Теперь я дал версию программы, которую могут читать компьютеры, давайте попробуем версию, которую могут читать люди. Я преобразовал байты, составляющие квинту, в кодовую страницу 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? Вы можете подумать, что добавление комментариев к квине просто усложняет ситуацию, но это соревнование в скорости (а не соревнование в размере), означающее, что комментарии не влияют на скорость программы. Тем временем Brain-Flak и, следовательно, Mini-Flak просто сбрасывают содержимое стека на стандартный вывод; если бы вы должны были убедиться, что стек содержит толькоСимволы, составляющие команды вашей программы, вам придется тратить циклы на очистку стека. Как таковой, Brain-Flak игнорирует большинство символов, так что пока мы гарантируем, что элементы стека нежелательной почты не являются действительными командами Brain-Flak (делая это полиглотом Brain-Flak / Mini-Flak), и не являются отрицательными или внешними диапазон Юникода, мы можем просто оставить их в стеке, разрешить их вывод и поместить один и тот же символ в нашу программу в то же место, чтобы сохранить свойство quine.
Есть один особенно важный способ, которым мы можем воспользоваться этим. Quine работает с использованием длинной строки данных, и в основном все выходные данные из Quine создаются путем форматирования строки данных различными способами. Есть только одна строка данных, несмотря на то, что программа состоит из нескольких частей; поэтому мы должны иметь возможность использовать одну и ту же строку данных для печати разных частей программы. Трюк «ненужные данные не имеет значения» позволяет нам сделать это очень простым способом; мы сохраняем символы, составляющие программу, в строке данных, добавляя или вычитая значение в или из их кода ASCII. В частности, символы, составляющие начало программы, сохраняются как их код ASCII + 4, символы, составляющие раздел, который повторяется почти 241 раз, как их код ASCII - 4,каждый символ строки данных со смещением; если, например, мы распечатаем его с 4, добавленными к каждому символьному коду, мы получим один повтор повторяющегося раздела с некоторыми комментариями до и после. (Эти комментарии - просто другие разделы программы, с кодами символов, смещенными так, что они не образуют никаких допустимых команд Brain-Flak, потому что было добавлено неправильное смещение. Мы должны уклоняться от команд Brain-Flak, а не только от Mini- Flak команды, чтобы избежать нарушения части вопроса с ограниченным источником ; выбор смещений был разработан, чтобы гарантировать это.)
Из-за этого трюка с комментариями нам фактически нужно иметь возможность выводить строку данных, отформатированную двумя разными способами: а) кодировать так же, как в исходном коде, б) как коды символов с указанным смещением, добавляемым к каждому коду. Это огромное упрощение, которое делает добавленную длину полностью стоящей.
Структура программы
Эта программа состоит из четырех частей: вступление, строка данных, средство форматирования строки данных и выход. Вступление и завершение в основном отвечают за запуск строки данных и ее средства форматирования в цикле, каждый раз указывая соответствующий формат (т. Е. Кодировать или смещать и какое смещение использовать). Строка данных - это просто данные, и это единственная часть квин, для которой символы, из которых она состоит, не указываются буквально в строке данных (это, очевидно, было бы невозможно, так как это должно быть длиннее, чем само по себе); таким образом, оно написано так, что его особенно легко восстановить. Модуль форматирования строки данных состоит из 241 практически идентичных частей, каждая из которых форматирует конкретный элемент данных из 241 в строке данных.
Каждая часть программы может быть создана с помощью строки данных и ее средства форматирования следующим образом:
- Чтобы создать outro, отформатируйте строку данных со смещением +8
- Чтобы создать форматировщик строки данных, отформатируйте строку данных со смещением +4, 241 раз
- Чтобы создать строку данных, отформатируйте строку данных с помощью ее кодирования в исходный формат.
- Для создания вступления отформатируйте строку данных со смещением -4
Поэтому все, что нам нужно сделать, это посмотреть, как работают эти части программы.
Строка данных
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …
Нам нужна простая кодировка для строки данных, поскольку мы должны иметь возможность изменить кодировку в коде Mini-Flak. Вы не можете стать намного проще, чем это!
Ключевая идея этого лозунга (кроме трюка с комментариями) заключается в том, чтобы заметить, что существует только одно место, в котором мы можем хранить большие объемы данных: «суммы возвращаемых значений команд» в пределах различных уровней вложенности исходного кода программы. (Это обычно называют третьим стеком, хотя у Mini-Flak нет второго стека, поэтому «рабочий стек», вероятно, является лучшим именем в контексте Mini-Flak.) Другими возможностями хранения данных будет основной / первый стек (который не работает потому что именно туда должны идти наши выходные данные, и мы не можем удаленно перемещать выходные данные за пределы хранилища) и кодировать их в bignum в одном элементе стека (что не подходит для этой проблемы, поскольку для этого требуется экспоненциальное время извлечь из него данные); когда вы их устраняете, рабочий стек остается единственным местом.
Чтобы «хранить» данные в этом стеке, мы используем несбалансированные команды (в данном случае, первую половину (…)
команды), которые позже будут сбалансированы в формататоре строк данных. Каждый раз, когда мы закрываем одну из этих команд в модуле форматирования, она выталкивает сумму данных, взятых из строки данных, и возвращает значения всех команд на этом уровне вложенности в модуле форматирования; мы можем гарантировать, что последние добавляются к нулю, поэтому форматировщик просто видит отдельные значения, взятые из строки данных.
Формат очень прост: (
за ним следуют n копий ()
, где n - это число, которое мы хотим сохранить. (Обратите внимание, что это означает, что мы можем хранить только неотрицательные числа, и последний элемент строки данных должен быть положительным.)
Один немного неинтуитивный момент в отношении строки данных - это порядок, в котором она находится. «Начало» строки данных - это конец ближе к началу программы, то есть самый внешний уровень вложенности; эта часть форматируется последней (поскольку форматировщик работает от самого внутреннего до самого внешнего уровня вложенности). Однако, несмотря на то, что форматируется последним, он печатается первым, потому что значения, помещенные в стек первым, печатаются последними интерпретатором Mini-Flak. Тот же принцип применяется к программе в целом; нам нужно сначала отформатировать outro, затем форматер строки данных, затем строку данных, затем intro, то есть в обратном порядке, в котором они хранятся в программе.
Форматирование строки данных
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
Модуль форматирования строки данных состоит из 241 раздела, каждый из которых имеет идентичный код (один раздел имеет незначительно отличающийся комментарий), каждый из которых форматирует один конкретный символ строки данных. (Мы не могли использовать цикл здесь: нам нужен несбалансированный )
для чтения строки данных путем сопоставления его несбалансированным (
, и мы не можем поместить один из них в {…}
цикл, единственную форму цикла, которая существует. Поэтому вместо этого мы " разверните «форматтер» и просто заставьте вступление / выход для вывода строки данных со смещением форматера 241 раз.)
)[({})( … )]{}
Самая внешняя часть элемента форматирования считывает один элемент строки данных; простота кодирования строки данных приводит к небольшой сложности при ее чтении. Мы начинаем с закрытия несопоставленного значения (…)
в строке данных, а затем отрицаем ( […]
) два значения: данные, которые мы только что прочитали из строки данных ( ({})
), и возвращаемое значение остальной части программы. Мы копируем возвращаемое значение остальной части элемента форматирования с помощью (…)
и добавляем копию в отмененную версию с {}
. Конечным результатом является то, что возвращаемое значение элемента строки данных и элемента форматирования вместе представляет собой элемент данных минус элемент данных минус возвращаемое значение плюс возвращаемое значение или 0; это необходимо, чтобы следующий элемент строки данных выдал правильное значение.
([({})]({}{}))
Модуль форматирования использует верхний элемент стека, чтобы узнать, в каком режиме он находится (0 = формат в форматировании строки данных, любое другое значение = смещение для вывода). Тем не менее, просто прочитав строку данных, элемент данных находится поверх формата в стеке, и мы хотим, чтобы они были наоборот. Этот код является более коротким вариантом кода обмена Brain-Flak, взяв a от b до b выше a + b ; он не только короче, но и (в данном конкретном случае) более полезен, поскольку побочный эффект от добавления b к a не вызывает проблем, когда b равно 0, а когда b не равно 0, он выполняет расчет смещения для нас.
{
((()[()]))
}{}
{
…
((()[()]))
}{}
Brain-Flak имеет только одну структуру потока управления, поэтому, если нам нужно что-то кроме while
цикла, это займет немного работы. Это структура "отрицания"; если в стеке есть 0, он удаляет его, в противном случае он помещает 0 в верхнюю часть стека. (Это работает довольно просто: до тех пор, пока на стеке нет 0, дважды нажмите 1 - 1 в стек; когда вы закончите, вытолкните верхний элемент стека.)
Как показано здесь, можно поместить код в отрицательную структуру. Код будет выполняться только в том случае, если вершина стека отлична от нуля; так что если у нас есть два отрицающих структуры, предполагая , что два верхних элемента стека не как ноль, они компенсируют друг друга, но любой код внутри первой структуры будет работать только тогда , когда элемент верхнего стека был отличен от нуля, и код внутри вторая структура будет работать, только если верхний элемент стека был равен нулю. Другими словами, это эквивалент оператора if-then-else.
В предложении «then», которое выполняется, если формат ненулевой, нам фактически нечего делать; нам нужно поместить данные + смещение в основной стек (чтобы его можно было выводить в конце программы), но оно уже есть. Таким образом, мы имеем дело только со случаем кодирования элемента строки данных в исходной форме.
{
({}(((({}())[()])))[{}()])
}{}
(({}))
Вот как мы это делаем. {({}( … )[{}()])}{}
Структура должна быть знакома как цикл с определенным числом итераций (который работает путем перемещения счетчика цикла к рабочей стеке и удерживая ее там, она будет в безопасности от любого другого кода, так как доступ к рабочей стеке привязан к уровень вложенности программы). Тело цикла is ((({}())[()]))
, которое делает три копии верхнего стекового элемента и добавляет 1 к нижнему. Другими словами, он превращает 40 на вершине стека в 40 выше 40 выше 41, или рассматривается как ASCII, (
в (()
; работает этот раз будет делать (
в (()
в (()()
в (()()()
и так далее, и , таким образом , представляет собой простой способ для создания нашей строки данных (при условии , что есть (
на вершине стека уже).
Как только мы закончим с циклом, (({}))
дублирует верхнюю часть стека (так, чтобы он теперь начинался, ((()…
а не (()…
. Ведущий (
будет использоваться следующей копией форматера строки данных для форматирования следующего символа (он развернет его в (()(()…
затем (()()(()…
и так далее, так что это создает разделение (
в строке данных).
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'
Есть еще один интерес в форматере строк данных. Итак, в основном это всего лишь 4 сдвинутых вниз кодовых точки; однако этот апостроф в конце может показаться неуместным. '
(кодовая точка 39) переместится в +
(кодовая точка 43), которая не является командой Brain-Flak, так что вы, возможно, догадались, что она существует для какой-то другой цели.
Причина этого заключается в том, что форматировщик строки данных ожидает, что (
в стеке уже есть (он нигде не содержит литерала 40). '
фактически находится в начале блока, который повторяется для создания средства форматирования строки данных, а не в конце, поэтому после того, как символы средства форматирования строки данных были помещены в стек (и код собирается перейти к печати строки данных сам) outro настраивает 39 на вершине стека в 40, готовый к форматтеру (самому работающему на этот раз форматеру, а не его представлению в источнике), чтобы использовать его. Вот почему у нас «почти 241» копий форматера; в первой копии отсутствует первый символ. И этот символ, апостроф, является одним из трех символов в строке данных, которые не соответствуют коду Mini-Flak где-то в программе; это просто как метод обеспечения константы.
Интро и аутро
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
…
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
Интро и аутро являются концептуально одной и той же частью программы; единственная причина, по которой мы проводим различие, заключается в том, что outro необходимо выводить до того, как строка данных и ее форматер (чтобы она печаталась после них), тогда как intro необходимо выводить после них (печатать до них).
(((()()()()){}))
Начнем с размещения двух копий по 8 в стеке. Это смещение для первой итерации. Вторая копия вызвана тем, что основной цикл ожидает, что в верхней части стека будет находиться нежелательный элемент выше смещения, оставленный позади теста, который решает, существует ли основной цикл, и поэтому нам нужно поместить туда ненужный элемент, чтобы это не выбрасывает элемент, который мы на самом деле хотим; копия - самый краткий (и поэтому самый быстрый для вывода) способ сделать это.
Есть и другие представления числа 8, которые не длиннее этого. Тем не менее, при работе с самым быстрым кодом, это определенно лучший вариант. С одной стороны, использование ()()()()
происходит быстрее, чем, скажем, (()()){}
потому что, несмотря на то, что оба имеют длину 8 символов, первый из них является циклом быстрее, поскольку (…)
считается как 2 цикла, но ()
только как один. Сохранение одного цикла незначительно по сравнению с гораздо большим соображением для квин , хотя: (
и )
имеют гораздо более низкие кодовые точки, чем {
и }
, поэтому генерация фрагмента данных для них будет намного быстрее (и фрагмент данных будет занимать меньше места в коде, слишком).
{{} … }{}{}
Основной цикл. Здесь не учитываются итерации (это 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. Вы можете быть смущены отсутствием ()
, которое обычно присутствует в константах Brain-Flak; это не обычное число, а церковное число, которое интерпретирует числа как операцию дублирования. Например, церковная цифра 60, показанная здесь, делает 60 копий своих входных данных и объединяет их все вместе в одно значение; в Brain-Flak единственное, что мы можем объединить, - это обычные числа, кроме того, поэтому мы в итоге добавляем 60 копий вершины стека и, таким образом, умножаем верх стека на 60.
В качестве примечания можно использовать искатель чисел Underload , который генерирует церковные цифры в синтаксисе Underload, чтобы найти соответствующее число и в Mini-Flak. Цифры недогрузки (отличные от нуля) используют операции «дублировать верхний элемент стека» :
и «объединить два верхних элемента стека» *
; обе эти операции существуют в Brain-Flak, так что вы просто переводите :
в )
, *
до {}
, {}
добавляете a и добавляете достаточно (
в начале, чтобы сбалансировать (это использует странное сочетание основного стека и рабочего стека, но это работает).
Этот конкретный фрагмент кода использует церковную цифру 60 (фактически, фрагмент «умножить на 60») вместе с приращением, чтобы сгенерировать выражение 60 x + 1. Таким образом, если у нас было 4 из предыдущего шага, это дает нам значение 241, или если у нас было 0, мы просто получаем значение 1, т.е. это правильно вычисляет количество итераций, которые нам нужны.
Выбор 241 не случаен; это значение было выбрано равным а) приблизительной длине, на которой программа все равно закончится, и б) 1 более чем в 4 раза больше круглого числа. Круглые числа, в данном случае 60, имеют тенденцию иметь более короткие представления в виде церковных чисел, потому что у вас больше гибкости в копировании факторов. Позже программа содержит отступы, чтобы довести длину до 241 точно.
{
({}(
…
)[{}()])
}{}
Это цикл for, подобный тому, который мы видели ранее, который просто запускает код внутри него количество раз, равное вершине основного стека (который он использует; сам счетчик цикла хранится в рабочем стеке, но видимость это связано с уровнем вложенности программы и, следовательно, невозможно взаимодействовать с ним, кроме самого цикла 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, поэтому цикл будет выходить, а не продолжаться на итерации впоследствии.