Используйте третий стек
Если вы прочитали название, вы можете быть немного смущены. Конечно, в Brain-Flak есть только два стека? Тем не менее, я вас уверяю, что он существует и является одним из самых мощных, если не самым мощным инструментом для написания и игры в гольф Brain-Flak.
Что такое «третий стек»?
Каждая программа Brain-Flak так или иначе использует третий стек, но большая часть использования происходит за кулисами, и часто полезно просто игнорировать тот факт, что он существует. Каждая скобка в программе либо добавляет, либо удаляет один элемент из стека. Три из открытых фигурных скобок ([<
добавляют элемент в стек, в то время как все три сопряженных )]>
элемента удаляют элемент из стопки. Значение элемента в стеке - это значение текущей области программы, и использование nilads изменит это значение определенным образом. Закрывающая круглая скобка )
имеет уникальную функцию перемещения элемента из третьего стека в текущий стек; толчок
Надеюсь, вам это станет ясно. Третий стек - это своего рода стек, который запоминает возвращаемые значения кода, который уже был выполнен. Давайте рассмотрим пример простой программы, отслеживающей два обычных стека и третий стэк.
пример
Мы пройдемся по следующей программе. Эта программа выталкивает -3, 1, -2
в стек.
(([()()()])(()))
Мы начинаем с трех открытых фигурных скобок, которые все толкают ноль в третий стек.
Теперь наши стеки выглядят так: третий стэк справа, а активный стек ^
под ним:
0
0
0 0 0
^
(([()()()])(()))
^
Теперь у нас есть три ()
нилада. Они ничего не делают с обычными двумя стеками, но каждый добавляет один к вершине третьего стека, делая наши стеки похожими на:
3
0
0 0 0
^
(([()()()])(()))
^
Теперь мы сталкиваемся с тем, что, ]
как указано выше, закрывающие скобки удаляют элемент из третьего стека, но у ]
него есть функция вычитания удаляемого элемента из вершины стека. Таким образом, наши новые стеки будут выглядеть так:
-3
0 0 0
^
(([()()()])(()))
^
Это имеет смысл; [...]
делает отрицание, поэтому ]
следует вычитать вниз.
Теперь мы должны выполнить )
. Как вы, вероятно, помните, )
это место в программе, где вещи помещаются в стек, поэтому мы будем перемещать вершину третьего стека в текущий стек, кроме того, мы добавим -3
элемент к следующему элементу в третьем стеке.
-3 0 -3
^
(([()()()])(()))
^
Еще раз мы сталкиваемся с одной из наших трех открытых фигурных скобок, поэтому мы добавим еще один элемент в наш третий стек.
0
-3 0 -3
^
(([()()()])(()))
^
Как мы уже говорили ранее ()
, вершина нашего третьего стека будет увеличиваться на единицу.
1
-3 0 -3
^
(([()()()])(()))
^
И )
переместит вершину третьего стека в активный стек и добавит вниз
1
-3 0 -2
^
(([()()()])(()))
^
Последний )
перемещает третий стек в активный стек, и поскольку в третьем стеке не осталось элементов для добавления, он больше ничего не делает.
-2
1
-3 0
^
(([()()()])(()))
^
Программа окончена, поэтому мы завершаем и выводим.
Этот пример предназначен для того, чтобы дать вам представление о том, что такое третий стек. Он не включает все операции, но, надеюсь, вы сможете выяснить, что каждая из них делает самостоятельно. Если вы все еще боретесь, я включил «шпаргалку» в нижней части этого ответа, чтобы помочь вам в этом.
Хорошо, и что?
Хорошо, теперь вы понимаете третий стек, но «и что»? Вы уже использовали его, даже если не называли его «третьим стеком», как мышление в терминах третьего стека помогает вам играть в гольф?
Давайте посмотрим на проблему. Вы хотите взять Треугольник числа . Это сумма всех чисел меньше n.
Один из подходов может состоять в том, чтобы создать аккумулятор в оффстаке и добавить к нему по мере обратного отсчета. Это создает код, который выглядит следующим образом:
(<>)<>{(({}[()])()<>{})<>}{}<>({}<>)
Попробуйте онлайн!
Этот код довольно компактен, и можно подумать, что он не может быть намного меньше. Однако если мы подойдем к нему с точки зрения третьего стека, станет ясно, что это крайне неэффективно. Вместо того, чтобы положить наш аккумулятор в офсет, мы можем поместить его в третий стек с помощью a (
и получить его в конце, который мы используем )
. Мы еще раз пройдемся по всем числам, но на этот раз нам не нужно ничего делать, чтобы увеличить наш третий стек, программа делает это для нас. Это выглядит так:
({()({}[()])}{})
Попробуйте онлайн
Этот код меньше половины размера довольно хорошей версии для гольфа, которую мы сделали ранее. Фактически компьютерный поиск доказал, что эта программа является самой короткой из возможных программ, способных выполнить эту задачу. Эта программа может быть объяснена с использованием подхода «сумма всех прогонов», но я думаю, что она намного более интуитивна и понятна, когда объясняется с использованием подхода третьего стека.
Когда я использую третий стек?
В идеале, всякий раз, когда вы начинаете работу над новой проблемой в Brain-Flak, вы должны подумать про себя, как бы я сделал это с учетом Третьего стека. Однако, как общее практическое правило, когда вам нужно отслеживать какой-то тип аккумулятора или иметь промежуточную сумму, будет хорошей идеей попытаться поместить это в свой третий стек вместо двух реальных стеков.
В другой раз, возможно, стоит подумать об использовании вашего третьего стека, когда у вас нет места для хранения какого-либо значения в двух других стеках. Это может быть особенно полезно, когда вы выполняете манипуляции с двумя существующими стеками и хотите сохранить значение для последующего использования без необходимости отслеживать, где оно находится.
Ограничения третьего стека
Третий стек очень силен во многих отношениях, но у него есть свои ограничения и недостатки.
Во-первых, максимальная высота стека для третьего стека в любой заданной точке определяется во время компиляции. Это означает, что если вы хотите использовать объем пространства в стеке, вы должны выделить это пространство при написании программы.
Во-вторых, третий стек - это не произвольный доступ. Это означает, что вы не можете выполнять какие-либо операции с любым значением, кроме самого верхнего значения. Кроме того, вы не можете перемещать значения в стеке (скажем, поменять местами первые два элемента).
Вывод
Третий стек - это мощный инструмент, и я считаю его необходимым для каждого пользователя Brain-Flak. Это требует некоторого привыкания и изменения вашего мышления о программировании в Brain-Flak, но при правильном использовании это делает разницу между приличным и удивительным, когда дело доходит до игры в гольф.
Cheatsheet
Вот список операций и как они влияют на третий стек
Operation | Action
====================================================
(,[,< | Put a zero on top of the Third Stack
----------------------------------------------------
) | Add the top of the Third Stack to the
| second element and move it to the
| active stack
----------------------------------------------------
] | Subtract the top of the Third Stack
| from the second element and pop it
----------------------------------------------------
> | Pop the top of the Third Stack
----------------------------------------------------
() | Add one to the top of the Third Stack
----------------------------------------------------
{} | Pop the top of the active stack and
| add it to the top of the Third Stack
----------------------------------------------------
[] | Add the stack height to the Third
| Stack
----------------------------------------------------
<>,{,} | Nothing
{...}
возвращает сумму всех итераций.<>,{,} | Nothing
Нахождение модуля / остатка
Поиск n по модулю m является одной из основных арифметических операций, важных для многих задач. Для случаев m> 0 и n> = 0 может использоваться следующий 46-байтовый фрагмент. Предполагается, что n находится сверху активного стека, а m - следующий, и заменяет их на n mod m . Оставляет остальные стеки нетронутыми.
Эта аннотированная версия показывает содержимое стека в некоторых точках программы.
;
разделяет два стека и.
отмечает активный стек.источник
{({}[()])<>}
), но однажды я понял это ... Genius :-)Push-Pop Redundancy
Это большой. Это также немного нюанс.
Идея состоит в том, что если вы что-то толкаете, а затем выталкиваете, ничего не делая, вам вообще не следовало толкать это.
Например, если вы хотите переместить что-то в offstack, а затем добавить к нему, вы можете написать следующий код:
Это может быть проще, как это:
Первая программа поднимает предмет, перемещает его, снова поднимает и добавляет один, а вторая делает оба за один раз.
Этот пример был прост, однако он может стать намного сложнее. Взять, к примеру:
Уменьшение здесь менее очевидно, но 4-е всплывающее окно в программе можно уменьшить с помощью 2-го нажатия, например, так:
Это самый мощный инструмент в моем репертуаре для игры в гольф, но для того, чтобы преуспеть в этом, требуется некоторая практика. Как только вы сделаете это некоторое время, вы сможете обнаружить их почти мгновенно.
источник
({}<{}>)
?({}<{}>)
этом полностью уничтожает элемент. Однако эти программы не должны были быть оптимальными только для того, чтобы подчеркнуть принцип работы здесь.Оптимизируйте свои целые числа
Целые числа утомительно представлять в Brain-Flak. К счастью, у нас есть вопрос, который поможет в игре Golf a Brain-Flak Integer . (Обратите внимание, что вопрос предназначен для помещения целого числа в стек, поэтому избыточность push-pop, вероятно, применима к более реалистичным программам.)
источник
Нажмите дополнительные счетчики петли
Часто вы хотите сделать что-то вроде
или
Когда ввод может содержать 0 или результат операции X может дать 0, это действительно неудобно. Потому что вам нужно сделать:
Делать X каждому элементу, а потом
Чтобы снова перевернуть массив.
Еще хуже, если мы оперируем парами смежных элементов, нам нужно сделать
([][()])
вместо них([])
. Это действительно неудобно. Вот хитрость: когда вы делаете X для каждого элемента, нажмите 1 на альтернативный стек прямо надX(element)
. Затем, пока вы меняете его, вы можете просто сделатьЭто на 8 байт короче, поэтому, когда вы добавите дополнительный код для добавления 1, вы сэкономите 4-6 байт. Для более конкретного примера сравните задачу получения дельт массива. Без этого трюка вам понадобится:
Для 62. С этим трюком у вас будет
Для 58. Если использовать правильный путь (например, сначала
([][()])
поменять местами , а потом удалить два ), это может сэкономить еще больше в особых сценариях.источник
Воспользуйтесь ниладой высоты стека
Особенно в задачах колмогоровской сложности или задачах, где вы всегда знаете размер входных данных, вы можете использовать преимущество «Stack-Height»
[]
для создания целых чисел.Давайте разберемся с этим с гипотетической задачей: вывод
CAT
. Способ не для игры в гольф состоит в том, чтобы использовать онлайн-целочисленного игрока в гольф 67, 65 и 84. Это дает:(Новые строки для ясности). Это 88 байтов, и не так уж и здорово. Если вместо этого мы выдвигаем последовательные различия между значениями, мы можем сэкономить много. Итак, мы заключаем первый номер в push- вызов и вычитаем 2:
Затем мы берем этот код, заключаем его в push- вызов и добавляем 19 в конец:
Это 62 байта для колоссального 26 байтового гольфа!
Теперь здесь , где мы получаем , чтобы воспользоваться стек высотой nilad. К тому времени, когда мы начнем нажимать 19, мы знаем, что в стеке уже есть 2 элемента, поэтому
[]
оценим их2
. Мы можем использовать это для создания 19 байтов меньше. Очевидным способом является изменение внутреннего()()()
на()[]
. Это только экономит два байта. Оказывается, с еще большим количеством поворотов мы можем нажать 19 сЭто экономит нам 6 байтов. Теперь мы до 56.
Вы можете видеть, что этот совет очень эффективно используется в следующих ответах:
Hello World V1
Hello World V2
Магическое Собрание, Друг или Враг
Выведите недостающее целое число
Диагональный алфавит (мой личный фаворит)
источник
CAT
программа на самом деле толкаетTAC
: P[]
: предваряйте0
s с помощью(<>)
s перед вашим кодом. Это действительно работает только в том случае, если вы все равно планируете сдвинуть код в обратном направлении, но если вы найдете правильное число, вы можете немного уменьшить свой код. Довольно экстремальный пример, где я добавляю 60
с, что позволяет мне использовать столько же,[]
сколько я использую()
Используйте вики
У нас есть вики ! У него есть некоторые недостатки, но это полезный ресурс. В нем есть списки полезных, хорошо отлаженных, чистых программ, которые вы можете вставить в свой код. Если вы хотите сделать что-то, что, по вашему мнению, кто-то мог сделать до того, как есть вероятность, что это в вики.
источник
Лучше зацикливание
Вот простой:
Довольно распространенная конструкция:
Там, где вы хотите сделать n циклов, но при этом сохранить n. Однако это можно записать так:
сохранить 2 байта.
Еще одна довольно распространенная парадигма
Который будет зацикливаться и накапливать весь стек. Из-за какой-то необычной математики это так же, как:
источник
Проверьте свои негативы
Иногда вы можете сыграть в гольф несколько байтов, стратегически выбирая, что отрицать с помощью
[...]
монады.Простой пример во вложенных
[...]
s. Например[()()()[()()]]
может быть просто[()()()]()()
Допустим, вы хотите проверить, является ли значение любым из начальных скобок
(<{[
. Первоначальная попытка состоит в том, чтобы увеличить разницу между каждым символом и циклом, вычитая егоОднако вы можете сэкономить 4 байта, вместо этого отправив отрицательные версии различий!
Как правило, это не сильно вас экономит, но также требует очень мало усилий, чтобы изменить окружающий
[...]
мир. Будьте внимательны к ситуациям, когда вы можете нажать отрицательное значение счетчика вместо положительного, чтобы сэкономить на увеличении несколько раз, а не уменьшении позже. Или выгрузив(a[b])
с ,([a]b)
чтобы сделать разницу между двумя отрицательными номерами вместо позитива.источник
<a<b>c>
-><abc>
и<a[b]c>
-><abc>
.