Поради щодо гольфу в Brain-Flak


24

Brain-flak - це на основі стека мова тюрінг-тарпітів, написана спільно між мною, DJMcMayhem та 1000000000 .

Деякі користувачі дуже досвідчені в таємничих способах Brain-Flak. Тож я вважав, що це гарна ідея поставити це питання як спосіб для нас, і, сподіваємось, і інших, поділитися нашими знаннями з громадою та знизити смугу вступу до цієї мови, "призначеної для болючого використання". І, можливо, навіть навчіть тих, хто має більший досвід, деяким новим хитрощам.

Отже, які поради ви маєте для гольфу в мозку? Я шукаю ідеї, які можна застосувати до проблем із гольфом взагалі, які принаймні дещо характерні для мозку (наприклад, "видалити коментарі" - це не відповідь).

Будь ласка, опублікуйте одну пораду за кожну відповідь.

Відповіді:


22

Використовуйте третій стек

Якщо ви прочитали заголовок, ви можете трохи розгубитися. Напевно, у Brain-Flak є лише два стеки? Однак я запевняю вас, що він існує, і це один з найпотужніших, якщо не найпотужніший інструмент для письма та гольфу Brain-Flak.


Що таке "Третя стека"?

Кожна програма Brain-Flak так чи інакше використовує третій стек, але більша частина використання продовжується за лаштунками, і часто корисно просто ігнорувати факт його існування. Кожна дужка в програмі або додає, або видаляє один елемент із стеку. Три відкритих дужки ([<всі додають елемент у стек, а їх три кон'югати )]>видаляють елемент із стека. Значення елемента на стеці - це значення поточного обсягу програми та використання nilads певним чином модифікує це значення. Близькі дужки )мають унікальну функцію переміщення елемента з третього стеку до поточного стеку; поштовх.

Сподіваємось, це вам стає зрозумілим. Третій стек - це якийсь стек, який запам'ятовує повернені значення коду, які вже були виконані. Давайте розглянемо приклад простої програми, яка відстежує два нормальних стеки та Третій стек.

Приклад

Ми пройдемо наступну програму. Ця програма підштовхується -3, 1, -2до стеку.

(([()()()])(()))

Починаємо з трьох відкритих дужок, які всі просувають нуль до третьої стеки.

Наші стеки зараз виглядають так: Третій стек знаходиться праворуч, а активний стек має ^під ним:

        0
        0
  0  0  0
  ^

(([()()()])(()))
   ^

Зараз у нас є три ()нилади. Вони не роблять нічого для звичайних двох стеків, проте кожен додає по одній до вершини Третьої стеки, роблячи наші стеки схожими:

        3
        0
  0  0  0
  ^

(([()()()])(()))
         ^

Зараз ми стикаємося з тим, ]як було сказано перед тим, як закриті дужки видаляють елемент з Третього стеку, але він ]має функцію віднімання елемента, який він видаляє, у верхній частині стека. Таким чином, наші нові стеки будуть виглядати так:

       -3
  0  0  0
  ^

(([()()()])(()))
          ^

Це має сенс; [...]чи заперечення так ]має відняти вниз.

Тепер ми повинні виконати a ). Як ви, напевно, пам'ятаєте, )це місце в програмі, де матеріал пересувається до стеку, тому ми будемо переміщувати верхню частину Третього стеку до поточної стеки, крім того, ми додамо -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, але при правильному використанні це робить різницю між пристойним і дивовижним, коли справа стосується гольфу.

Шпаргалку

Ось перелік операцій та те, як вони впливають на Третій стек

 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

1
Нічого собі, фантастична порада! Я насправді просто приходив сюди, щоб написати подібну відповідь, коли побачив це. +1! Я вважаю за краще вважати це накопичувачем , але метафора третього стеку справді цікава.
DJMcMayhem

Я завжди називав це "робочим простором" або "робочим столом".
sagiksp

У версії TIO Brainflak {...}повертається сума всіх ітерацій.
CalculatorFeline

@CalculatorFeline Так, це стосується майже всіх версій Brain-Flak, за винятком деяких дуже ранніх. Однак я не впевнений, чому ви прокоментували це саме на цій посаді.
Пшеничний майстер

<>,{,} | Nothing
CalculatorFeline

21

Пошук модуля / залишку

Пошук n модуля m - одна з основних арифметичних операцій, важлива для багатьох завдань. У випадках m> 0 і n> = 0 може бути використаний наступний 46-байтний фрагмент. Він передбачає, що n знаходиться на вершині активного стека з m наступним вниз, і замінює їх на n mod m . Він залишає решту стежок недоторканими.

({}(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]{})

Ця помічена версія показує вміст стеку в деяких точках програми. ;відокремлює дві стеки і .позначає активний стек.

. n m;
({}(<>))<>
{   . m; r 0   (r = n - km)
    (({}))
    . m m; r 0
    {({}[()])<>}
    {}
}
m-n%m-1 m; . 0
{}<>([{}()]{})
. n%m;

Мені знадобилось певний час, щоб зрозуміти, що робила ненаголошена частина ( {({}[()])<>}), але одного разу я зрозумів це ... Геній :-)
ETHproductions

11

Редукція Push-Pop

Це великий. Це також трохи нюанс.

Ідея полягає в тому, що якщо ти щось штовхаєш, а потім вискакуєш, не роблячи нічого, ти взагалі не повинен був би його підштовхувати.

Наприклад, якщо ви хочете щось перенести в офшпак, а потім додати його, ви можете написати наступний код:

({}<>)({}())

Це може бути простіше, як це:

({}<>())

Перша програма підхоплює елемент, переміщує його, знову піднімає його і додає один, а друга - обома.

Цей приклад був простим, проте він може отримати набагато складніше. Візьмемо для прикладу:

({}<({}<>)><>)(<((()()()){}[((){}{})])>)

Зменшення тут менш чітке, але 4-й поп в програмі можна зменшити за допомогою 2-го натискання, наприклад:

(<((()()()){}[((){}<({}<>)><>{})])>)

Це найпотужніший інструмент у моєму гольф-репертуарі, але для того, щоб добре в ньому зайнятися, потрібна певна практика. Як тільки ви це зробите деякий час, ви зможете помітити їх майже миттєво.


В останньому прикладі, чи не еквівалентна частина в першій парі дужок ({}<{}>)?
feersum

@feersum Ні, це не так. Він переміщує копію другого елемента в стеку до вимкненого стека, при ({}<{}>)цьому повністю знищує елемент. Однак насправді ці програми не були оптимальними лише для висвітлення принципу роботи.
Пшеничний майстер

6

Оптимізуйте цілі числа

Цілі особи нудно представляти в «Брейн-Флак». На щастя, у нас є питання, яке допоможе Golf Golf Brain-Flak Integer для вас. (Зверніть увагу, що питання розроблено для виштовхування цілого числа до стеку, тому надмірність push-pop, ймовірно, стосується більш реалістичних програм.)


Зауважте, що у нас також є brain-flak.github.io/integer/ , який для зручності запускає один із таких алгоритмів в Інтернеті.
DJMcMayhem

@DrMcMoylex Все, що нам зараз потрібно, як для реалізації цілого метагольфера в самому Brain-Flak!
Ніл


5

Висуньте зайві лічильники петлі

Часто вам захочеться зробити щось подібне

Виконайте операцію X на кожному елементі стеку

або

Виконайте операцію X на кожній парі сусідніх елементів у стеці

Якщо вхід може містити 0, або результат операції X може дати 0, це справді незручно. Тому що вам потрібно буде зробити:

([])
{

  {}

  ({}...<>)
  ([])

}

Щоб зробити X кожному елементу, а потім пізніше

<>
([])
{
  {}
  ({}<>)
  <>
  ([])
}
<>

Щоб знову повернути масив.

Ще гірше, якщо ми будемо працювати над парами сусідніх елементів, нам це потрібно зробити ([][()])замість ([]). Це справді незручно. Ось хитрість. Хоча ви робите X для кожного елемента, натисніть 1 на альтернативний стек прямо над X(element). Потім, поки ви реверсуєте це, ви можете просто зробити

<>
{
  {}
  ({}<>)
  <>
}
<>

Це на 8 байт коротше, тож якщо ви вкажете додатковий код для натискання на 1, ви збережете 4-6 байт. Для більш конкретного прикладу порівняйте завдання отримання дельт масиву. Без цього фокусу вам знадобиться:

([][()]){

    {}

    ([{}]({})<>)<>

    ([][()])

}{}{}<>

([])
{{}({}<>)<>([])}<>

Для 62. З цим фокусом у вас буде

([][()]){

    {}

    ([{}]({})<>)(())<>

    ([][()])

}{}{}<>

{{}({}<>)<>}<>

Для 58. Якщо використовувати правильний шлях (наприклад, перевернути спочатку і видалити два ([][()])пізніше), це може заощадити ще більше в конкретних сценаріях.


3

Скористайтеся перевагою накладки "Stack-Height"

Особливо в або викликах, коли ви завжди знаєте розмір вхідних даних, ви можете скористатися вкладкою "Stack-Height" []для створення цілих чисел.

Давайте попрацюємо над цим з гіпотетичним викликом: вихід CAT. Неголим способом є використання онлайн-гольфіста для цілих онлайн, щоб натиснути 67, 65 та 84. Це дає:

(((((()()()()){}){}){}()){}())

(((((()()()()){}){}){}){}())

((((((()()()){}()){}){})){}{})

(Нові рядки для наочності). Це 88 байт, і не настільки чудово. Якщо ми замість цього висунемо послідовні відмінності між значеннями, ми можемо зекономити багато. Таким чином, ми загортаємо перше число під натисканням і віднімаємо 2:

(   (((((()()()()){}){}){}()){}())  [()()] )

Потім ми беремо цей код, загортаємо його в push- дзвінок і додаємо 19 до кінця:

(  ((((((()()()()){}){}){}()){}())[()()])   ((((()()()){})){}{}()) )

Це 62 байти, за цілих 26 байт гольфу!

Тепер ось , де ми можемо скористатися вершиною висоти штабеля. На той момент, коли ми починаємо натискати на 19, ми знаємо, що на стеці вже є 2 елементи, тому []оцінюємо 2. Ми можемо використовувати це для створення 19 в меншій кількості байтів. Очевидним способом є зміна внутрішнього()()() на ()[]. Але це економить лише два байти. З деяким майстерністю, виявляється, ми можемо просунути 19

((([][]){})[]{})

Це економить нам 6 байт. Зараз ми знизилися до 56.

Ви можете побачити, що ця порада дуже ефективно використовується в цих відповідях:


Ваша CATпрограма насправді штовхає TAC: P
Wheat Wizard


2
Я знаю, що це підказка, але я не можу допомогти собі, 50 байт .
Пшеничний майстер

1
Ще дивно , але іноді ефективний рада для зловживання допомогою []: випереджаючи 0сек з (<>)с до вашого коду. Це дійсно спрацьовує лише в тому випадку, якщо ви все-таки плануєте відсунути код у зворотному порядку, але якщо ви знайдете потрібний номер, ви можете трохи зменшити код. Досить екстремальний приклад, коли я додаю 6 0s, що дозволяє мені використовувати стільки ж, скільки []і я()
Jo King

2

Використовуйте вікі

У нас є вікі ! Він має кілька коротких приходів, але це корисний ресурс. У ньому є списки корисних, добре гольфних, чистих програм, які ви можете вставити у свій код. Якщо ви хочете зробити щось, що, на вашу думку, хтось міг би зробити, перш ніж є хороший шанс, це на вікі.


2

Краще петлі

Ось такий простий:

Досить поширеною конструкцією є:

(({})<{({}[()]<...>)}{}>)

Де ви хочете циклічити n разів, але все одно тримайте n. Однак це можна записати так:

({<({}[()]<...>)>()}{})

зберегти 2 байти.

Ще одна досить поширена парадигма

([])({<{}>...<([])>}{})

Яка буде циклічно та накопичувати весь стек. Завдяки певній математиці це те саме, що:

(([]){[{}]...([])}{})

1

Перевірте свої негативи

Іноді ви можете пограти в кілька байтів, стратегічно вибираючи, що заперечувати [...] монаді.

Простий приклад - у вкладених [...]s. Наприклад, [()()()[()()]]просто може бути[()()()]()()

Скажіть, ви хочете перевірити, чи є значення будь-якого із стартових дужок (<{[. Початкова спроба полягає в пересуванні різниці між кожним символом і циклом над його відніманням

({}<(((((       #Push the differences between start bracket characters
(((()()()()){}){}){})   #Push 32
[()])                   #Push 31
[((()()())()){}{}])     #Push 20
){})><>)                #Push 40
<>({<(([{}]<>{}))>(){[()](<{}>)}<>})

Однак ви можете зберегти 4 байти, натиснувши натомість негативні версії відмінностей!

({}<(((((       #Push the differences between start bracket characters
((([()()()()]){}){}){}) #Push -32
())                     #Push -31
((()()())()){}{})       #Push -20
){})><>)                #Push -40
<>({<(({}<>{}))>(){[()](<{}>)}<>})

Як правило, це не дуже економить вас, але також коштує дуже мало зусиль, щоб змінити те, що [...]оточує. Будьте уважні до ситуацій, коли ви можете натиснути на мінус лічильника замість додатного, щоб заощадити на збільшенні кілька разів, а не зменшувати пізніше. Або вивантаживши (a[b])з , ([a]b)щоб зробити різницю між двома негативними номерами замість позитиву.


1
Подібні речі можна зробити і з нулями <a<b>c>-> <abc>і <a[b]c>-> <abc>.
Пшеничний майстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.