Brain-Flakでのゴルフのヒント


24

Brain-flakはスタックベースのチューリングターピット言語で、私とDJMcMayhem1000000000の間で共同で作成されました。

一部のユーザーは、Brain-Flakの神秘的な方法を非常に経験しています。ですから、この質問を私たち、そしてできれば他の人たちにとっても、コミュニティと知識を共有し、「使いにくいように設計された」この言語への入り口を低くする方法として設定することをお勧めします。そしておそらく、私たちに新しい経験をもっと教えてください。

それでは、ブレインフラックでゴルフをするためのヒントはありますか?私は、一般的にゴルフの問題をコーディングするのに適用できるアイデアを探しています。それは少なくとも脳のフレークに特有のものです(たとえば、「コメントの削除」は答えではありません)。

回答ごとに1つのヒントを投稿してください。

回答:


22

3番目のスタックを使用する

タイトルを読んでいると、少し混乱するかもしれません。Brain-Flakには2つのスタックしかありませんか?しかし、私はそれが存在することを保証します。それは、Brain-Flakを書いたりゴルフしたりするのに最も強力ではないにしても、最も強力なツールの1つです。


「サードスタック」とは何ですか?

すべてのBrain-Flakプログラムは何らかの方法で3番目のスタックを使用しますが、ほとんどの使用は舞台裏で行われ、多くの場合、存在するという事実を単に無視することが有用です。プログラム内の各括弧は、スタックから1つの項目を追加または削除します。3つの開いた中括弧は([<すべてスタックにアイテムを追加し、3つの共役は)]>すべてスタックからアイテムを削除します。スタック上のアイテムの値は、プログラムの現在のスコープの値であり、niladsを使用すると、特定の方法でこの値が変更されます。閉じ括弧に)は、要素を3番目のスタックから現在のスタックに移動する独自の機能があります。プッシュ。

うまくいけば、これはあなたに明らかになってきています。3番目のスタックは、既に実行されたコードの戻り値を記憶するある種のスタックです。2つの通常のスタックと3番目のスタックを追跡する単純なプログラムの例を見てみましょう。

次のプログラムについて説明します。このプログラム-3, 1, -2はスタックにプッシュします。

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

3つの開いたブレースから開始します。これらはすべて、3番目のスタックにゼロをプッシュします。

スタックは次のようになります。3番目のスタックが右側にあり、アクティブなスタックがその^下にあります。

        0
        0
  0  0  0
  ^

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

現在、3つの()niladがあります。これらは通常の2つのスタックには何もしませんが、それぞれ3つ目のスタックの最上部に1つずつ追加して、スタックを次のようにします。

        3
        0
  0  0  0
  ^

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

さて、]前に述べたように、閉じブレースは3番目のスタックからアイテムを削除します]が、スタックのトップから削除する要素を減算する機能を持っています。したがって、新しいスタックは次のようになります。

       -3
  0  0  0
  ^

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

意味あり; [...]否定を行うため、]下向きに減算する必要があります。

次に、を実行する必要があり)ます。おそらく思い出す)ように、スタック内に何かがプッシュされるプログラム内の場所なので、3番目のスタックのトップを現在のスタックに移動し、さらに-33番目のスタックの次の要素に追加します。

 -3  0 -3
  ^

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

ここでも、3つの開いたブレースの1つに遭遇するので、3番目のスタックに別の要素を追加します。

        0
 -3  0 -3
  ^

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

前に言ったよう()に、3番目のスタックの最上部を1つ増やします。

        1
 -3  0 -3
  ^

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

そして)、3番目のスタックの上部をアクティブスタックに移動し、下に追加します

  1
 -3  0 -2
  ^

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

最後)は3番目のスタックをアクティブスタックに移動し、3番目のスタックには追加する要素が残っていないため、他には何もしません。

 -2
  1
 -3  0
  ^

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

プログラムが終了したので、終了して出力します。


この例は、3番目のスタックが何であり、何であるかをユーザーに理解してもらうことを目的としています。すべての操作が含まれているわけではありませんが、各操作がそれ自体で何をしているのかを理解できることを願っています。まだ苦労している場合は、この回答の最後に「チートシート」を掲載しています。

OK、それで何?

さて、これでサードスタックは理解できましたが、「だから何?」「サードスタック」と呼ばなくても、すでに使用していましたが、サードスタックの観点から考えると、ゴルフにどのように役立ちますか。

問題を見てみましょう。あなたは数三角形を取りたいです。これは、nより小さいすべての数値の合計です。

1つのアプローチは、オフスタック上にアキュムレーターを作成し、カウントダウンしながらアキュムレーターを追加することです。これにより、次のようなコードが作成されます。

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

オンラインでお試しください!

このコードは非常にコンパクトであり、もっと小さくすることはできないと思うかもしれません。しかし、3番目のスタックの観点からアプローチすると、これは非常に非効率的であることが明らかになります。アキュムレータをオフスタックに配置する代わりに、aを使用して3番目のスタックに配置し、(使用する最後に取得することができます)。もう一度すべての数値をループしますが、今回は、Third Stackをインクリメントするために多くのことをする必要はありません。プログラムがそれを行います。これは次のようになります。

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

オンラインで試す

このコードは、以前に作成したかなりよくゴルフされたバージョンの半分以下のサイズです。実際、コンピューター検索により、このプログラムがこのタスクを実行できる最短のプログラムであることが証明されています。このプログラムは「すべての実行の合計」アプローチを使用して説明できますが、サードスタックアプローチを使用して説明すると、はるかに直感的で明確になると思います。

3番目のスタックはいつ使用しますか?

Brain-Flakで新しい問題の作業を開始するときはいつでも、サードスタックを念頭に置いてこれをどのように行うかを自分で考える必要があります。ただし、一般的な経験則として、あるタイプのアキュムレーターを追跡する必要がある場合、または積算合計がある場合は、2つの実際のスタックではなく、3番目のスタックに置くことをお勧めします。

3番目のスタックの使用を検討することをお勧めする別の機会は、他の2つのスタックに値を保存するスペースがない場合です。これは、2つの既存のスタックで操作を行っており、どこにあるかを追跡せずに後で使用するために値を保存する場合に特に役立ちます。

3番目のスタックの制限

Third Stackは多くの点で非常に強力ですが、独自の制限と欠点があります。

まず、特定のポイントでの3番目のスタックの最大スタック高さがコンパイル時に決定されます。つまり、スタック上の一定量のスペースを使用する場合は、プログラムを作成するときにそのスペースを割り当てる必要があります。

次に、3番目のスタックはランダムアクセスではありません。つまり、最上位の値以外の値に対して操作を実行することはできません。さらに、スタック上の値を移動することはできません(最初の2つの要素を入れ替えるなど)。

結論

Third Stackは強力なツールであり、すべての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!私はそれをThe Accumulatorと考えることを好みますが、The Third Stackのメタファーは本当に興味深いです。
DJMcMayhem

私は常に「ワークスペース」または「ワークスタック」と呼んでいました。
sagiksp

BrainflakのTIOバージョンで{...}は、すべての反復の合計を返します。
電卓

@CalculatorFelineはい、非常に初期のバージョンを除くBrain-Flakのほぼすべてのバージョンに当てはまります。しかし、特にこの投稿であなたがそれをコメントした理由はわかりません。
小麦ウィザード

<>,{,} | Nothing
電卓

21

モジュラス/剰余を見つける

mを法とするnを見つけることは、多くの課題にとって重要な基本的な算術演算の1つです。ケースについて> 0メートルN> = 0を、次の46バイトのスニペットを使用することができます。nはアクティブスタックの最上位にあり、mは次のスタックの下位にあると想定し、n mod mに置き換えます。残りのスタックはそのまま残ります。

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

この注釈付きバージョンは、プログラムのいくつかのポイントでスタックの内容を示しています。;2つのスタックを分離し.、アクティブなスタックにマークを付けます。

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

注釈のない部分が何をしたのかを理解するのにしばらく時間がかかりました({({}[()])<>})が、一度理解したら...天才:
ETHproductions

11

プッシュポップ冗長性

これは大きなものです。それは少し微妙なものでもあります。

アイデアは、何かをプッシュし、何もせずにポップした場合、まったくプッシュすべきではないということです。

たとえば、何かをオフスタックに移動してから追加する場合は、次のコードを作成します。

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

これは次のように簡単にすることができます。

({}<>())

最初のプログラムはアイテムを拾い上げて移動し、再び拾い上げて1つを追加します。2番目のプログラムは両方を一気に実行します。

この例は単純でしたが、もっと複雑になる可能性があります。例えば:

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

ここでの削減はそれほど明確ではありませんが、プログラムの4番目のポップは、次のように2回目のプッシュで削減できます。

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

これは私のゴルフのレパートリーの中で最も強力なツールですが、それを上手に使うにはある程度の練習が必要です。しばらくこれらをやっていると、これらをほとんど瞬時に見つけることができます。


後者の例では、括弧の最初のペアの部分は({}<{}>)?と同等ではありませんか?
-feersum

@feersumいいえ、そうではありません。スタック上の2番目のアイテムのコピーをオフスタックに移動({}<{}>)し、アイテムを完全に破壊します。しかし、これらのプログラムは、実際にここで動作している原則を強調するためだけに最適であることを意味していませんでした。
小麦ウィザード

6

整数を最適化する

整数はBrain-Flakで表現するのは面倒です。幸いなことに、Golf a Brain-Flak Integerに役立つ質問があります。(質問は整数をスタックにプッシュするように設計されているため、プッシュポップ冗長性はおそらくより現実的なプログラムに適用されることに注意してください。)


また、brain-flak.github.io / integer /があり、これらのアルゴリズムの1つをオンラインで実行するので便利です。
DJMcMayhem

@DrMcMoylex Brain-Flak自体に整数メタゴルファを実装する際に必要なものすべて!
ニール


5

追加のループカウンターをプッシュする

しばしば、あなたは次のようなことをしたいと思うでしょう

スタック内のすべての要素でX操作を実行します

または

スタック内の隣接する要素のすべてのペアでX操作を実行します

入力に「0」が含まれる場合、または操作Xの結果が0になる場合、これは本当に不便です。あなたがする必要があるので:

([])
{

  {}

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

}

各要素にXを実行し、その後で

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

配列を再び逆にします。

さらに悪いことに、隣接する要素のペアを操作する場合は([][()])、の代わりに行う必要があります([])。これは本当に不便です。コツは次のとおりです。各要素に対してXを実行しているときに、1をのすぐ上の代替スタックにプッシュしますX(element)。それから、あなたがそれを逆転させている間、あなたは単にすることができます

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

これは8バイト短いので、1をプッシュする追加のコードを考慮すると、4〜6バイトを節約できます。より具体的な例については、配列のデルタを取得するタスクを比較してください。このトリックがなければ、次のものが必要になります。

([][()]){

    {}

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

    ([][()])

}{}{}<>

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

62。このトリックを使えば、

([][()]){

    {}

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

    ([][()])

}{}{}<>

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

58の場合。正しい方法で使用した場合(たとえば、最初に元に戻し、2つ([][()])を後で削除するなど)、特定のシナリオでさらに節約できます。


3

「Stack-Height」niladを活用する

特にチャレンジ、または入力のサイズを常に知っているチャレンジでは、[]整数を作成するために「スタックの高さ」を使用できます。

仮想の課題であるoutputを使ってこれを処理してみましょうCAT。ゴルフ以外の方法は、オンライン整数ゴルファーを使用して67、65 、および84をプッシュすることです

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

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

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

(明確にするために改行)。これは88バイトで、それほど大きくありません。代わりに、値間の連続した差異をプッシュすると、多くを節約できます。したがって、プッシュコールで最初の数値をラップし、2を引きます。

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

次に、このコードを使用してプッシュコールでラップし、最後に19を追加します。

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

これは、なんと26バイトのゴルフの62バイトです!

さて、ここで私たちは、スタック高さniladを利用するために取得する場所です。19のプッシュを開始するまでに、スタックにはすでに2つのアイテムがあるため、[]評価はになり2ます。これを使用して、より少ないバイトで19を作成できます。明らかな方法は、インナー()()()をに変更すること()[]です。ただし、これにより2バイトしか節約されません。少し工夫して、19をプッシュできることがわかりました

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

これにより、6バイト節約できます。今、私たちは56人になりました。

これらの回答では、このヒントが非常に効果的に使用されていることがわかります。


あなたのCATプログラムは実際にプッシュしTACます:P
ウィートウィザード


2
私はこれがヒントであることを知っていますが、私は自分自身を助けることができません、50バイト
小麦ウィザード

1
悪用に役立つ別の奇妙だが時には効果的なヒント[]:コードの前に0sを(<>)sで追加する とにかくコードを逆方向にプッシュすることを計画している場合にのみ実際に機能しますが、適切な数に出会った場合は、コードをかなり減らすことができます。むしろ極端な例で、私は6追加0私は多くのとしてだけで使用することができますのを、[]私用としてS()
ジョー・キング

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
<>({<(({}<>{}))>(){[()](<{}>)}<>})

一般に、これはあなたをあまり節約しません[...]が、周囲の環境を変更するための労力もほとんどかかりません。後でデクリメントするのではなく、複数回インクリメントすることを節約するために、ポジティブではなくカウンターのネガをプッシュできる状況に注意してください。または、2つの数値の差を正ではなく負にするために(a[b])と交換し([a]b)ます。


1
ゼロ<a<b>c>-> <abc>および<a[b]c>-> でも同様のことができます<abc>
小麦ウィザード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.