Brainf*ck開発支援ツールを作った話

Brainf*ck開発支援というとありふれたインタプリタやデバッガが思い浮かぶかもしれませんが、作ったのは変数位置入れ替え補助です。
なんのこっちゃという人が多いと思うので説明します。

前提

Brainf*ckのコードを書くとき、

[直後と対応する]直前でデータポインタを変化させない

というルールを守ると人間に理解しやすいコードを書くことができます。
というのは、コード中のある操作がどのデータポインタ位置に対して行われているかが定まるからです。
言い方をかえると、データポインタ操作のかわりに高級言語の「変数」が使えるということになります。
参考:brainfuck入門

具体例

次のコードをみてください。上記のルールを守っていることがおわかりいただけると思います。

>> +
< [-
  >>> + <<<< [[- >>> + <<<] >>>> - <<<<
    >>> - <<<
  ] >>>> [-
    << - >>
    <<< [-] >>>
  ] < [- <<< + >>>] <<
]
<[-]

圧縮後:

>>+<[->>>+<<<<[[->>>+<<<]>>>>-<-<<<]>>>>[-<<-<[-]>>>]<[-<<<+>>>]<<]<[-]

以下、説明のためデータポインタが最初に指している場所を[0]と書き、そこからi番地進んだ場所を[i]と書きます。

これは[0]と[1]の値を比較し、[0] ≥ [1]であれば[2]をインクリメントするというプログラムです。
[0]~[4]までを使用し、[0]で終了します。
なお[0]と[1]の値は破壊されてしまいます。諸行無常ですね。

ここからが本題です。
あなたは[2]をインクリメントする条件を[1] ≥ [0]に書き換えたいと思ったとします。

たとえば[->>+<<]>[-<+>]>[-<+>]<<というコードを挿入することで[0]と[1]を入れ替えてしまうという手段がありますが、
これはコードが長くなってしまうのであまりエレガントではありません。

ここで、このプログラムは[,]前後でデータポインタが変化していないので、以下のようなコードに変換することができます。

ans++;
while (b) {
  b--;
  a2++;
  while (a) { //bfにおけるif文
    while (a) { a--; a1++; }  // aの値を破壊、a1に退避
    a2--;
    a1--; // 間接的にaをひとつ減らす
  }
  while (a2) {  // bfにおけるelse文
    a2--;
    ans--;
    while(b) { b--; }
  }
  while (a1) { a1--; a++; } // a1に退避した値をaに戻す
}
while (a) { a--; }

各変数を以下のようにメモリに割り当てたのが上記のBrainf*ckプログラムです。

[0][1][2][3][4]
abansa1a2

このabに割り当てる位置をひっくり返すことで目的のプログラムを作ることができます。
これが変数位置の入れ替えです。

この変換作業を人力で行うのはわりとつらいので、今回制作したツールが登場します。

使い方

使用イメージ
  1. コード入力エリアに変換したいbfのコードを貼り付ける
  2. 変数位置を設定する
  3. 「→」ボタンをおして変換

以上の3ステップで変数位置が書き換わったコードを得ることができました。やったね!

>>+<<[->>>>+<<<[[->>+<<]>>>-<-<<]>>>[-<<-<<[-]>>>>]<[-<<+>>]<<<]>[-]

適当な実行環境で確かめてみてください。
ちなみに私のオススメはBrainf**k インタープリタです。

使えない場合

前提で述べたルールを守っていないコードは変数の入れ替えができません。

>>+<<[[->+<]>>-<<]>[-<+>]>[++++++[-<+++++++>]<-.[-]>]<<
[[->>>+<<<]>>>>+[>++++++++++>[-]<[->>>+<<<<<[[->>>>+<<<<]>>>>>-<-<+<<<]>>>>>[-<<[->+<]<[-]<-<<->>>>>>]<[-<<<<+>>>>]<<]<<<+>>]+++++++[-<+++++++>]<-<]>[.<<]

たとえばこれは[0]を10進数で出力するわりと有用なコードですが、
ある[の直前と対応する]の直後でデータポインタが変化しているのでツールは使用不可能です。

余談

Brainf*ck力を磨くための適当な題材として競技プログラミングがあります。
なんとAtCoderは提出言語にBrainf*ckをサポートしています!
暇な人は過去問を解いてみるといいかもしれません。私は35問くらい解きました。

具体例で出したような汎用コードを作ってためておくことでBrainf*ck開発はずいぶん楽になります。
使えるコードのストックが増えていくのはわりと愉快なものです。
Brainf*ckはしばしば難解言語とか苦行とか言われますが、独特の楽しさがあります。まだ触ったことがない方、やってみませんか?

Posted by rodoni