brainfuckで数値を出力
d:id:tondol:20100630:1277839735の四則演算を利用してメモリ上の数値を10進数で出力してみました。例によって冗長なコードになっているので,実際のコードで利用(?)する場合は適宜最適化してやってください。
brainfuck
コメント中の「{@}」は下記の解説で登場する「ブロック」を表している気分です。「incr {@}」「decr {@}」などの表記は操作対象のブロックをひとつ右/左に移動するということを意味します。また,「{@0}」「{@1}」などの表記はブロック中の各区画を表している雰囲気です。
################################################## ### input ################################################## ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++ ################################################## ### algorithm ################################################## # {1} add !{0} [->+<] # while {@1} do >[< # {@2} add 10 >>++++++++++<< # devide {@1:@2} > >>>+[<<<[->>>>+>+<<<<<]>>>>>[-<<<<<+>>>>>]<<<<[->>>>+>+<<<<<]>>>>>[-<<<<<+>>>>>] <<[>[->+>+<<]>>[-<<+>>]<[[-]<->]<<-]+>[->+>+<<]>>[-<<+>>]<[[-]<<->>]<[[-]<<->>]< [[-]<<+<[->>>>>+>+<<<<<<]>>>>>>[-<<<<<<+>>>>>>]<[-<<<<<<->>>>>>]<<]<]<<[-]<[->+< ]>>[-<<+>>]<< < # {@3:@4} add !{@0} # {@0} add !{@4} # incr {@3} [->>>+>+<<<<] >>>>[-<<<<+>>>>]<<<< >>>+<<< # {@4} add !{@1} >[->>>+<<<]< # {@1} add !{@2} >>[-<+>]<< # incr {@} >>> # end >]< # decr {@} <<< # while {@0} do [ # {@1} add 48 # putchar {@1} >++++++++++++++++++++++++++++++++++++++++++++++++< >.< # decr {@} <<< # end ] # {@1} add 48 # putchar {@1} >++++++++++++++++++++++++++++++++++++++++++++++++< >.<
C言語風
ptr = 0; //メモリ上の0番地的な意味で ptr[0] = 128; ptr[1] = ptr[0]; ptr[0] = 0; while (ptr[1]) { ptr[2] = 10; divide(&ptr[1], &ptr[2]); //ptr[1]÷ptr[2]の商と剰余をptr[1],ptr[2]にそれぞれ代入 p[3] = p[0] + 1; p[4] = p[1]; p[1] = p[2]; ptr += 3; } ptr -= 3; while (ptr[0]) { ptr[1] += 48; putchar(ptr[1]); ptr -= 3; } ptr[1] += 48; putchar(ptr[1]);
解説
このプログラムの処理は大きく分けて2つの段階に分かれます。ひとつめはメモリを右方向へと移動(命令 '>' を実行)しながら,入力された(入力セクションでメモリの0番地に与えられた)値を各桁に分解し,メモリ上の各ブロックに格納する段階です。ふたつめはメモリを左方向へと移動(命令 '<' を実行)しながら,メモリ上の各ブロックに格納された各桁の値を出力する段階です。
説明中に登場した「ブロック」とは,3区画分のメモリをまとめて扱うための仮想的な概念です。ひとつのブロックはメモリ上の連続した3区画からなり,それぞれ左から「インデックス」「その桁の数値」「作業用スペース」として利用されます。ブロックは,メモリ上に連続して配置されており,たとえば,最初のブロックは0〜2番地,2つめのブロックは3番地〜5番地となります。
詳しくは解説しませんが,プログラムのひとつめの段階:分解フェーズでは,与えられた値を次々に10で割っていき,その余りを各ブロックに格納しています。与えられた値を10で割った結果が0になったら分解フェーズは終了です。分解フェーズが終了した段階で,メモリの内容は次のようになっているはずです。
[0][8][0]|[1][2][0]|[2][1][0]|[3][0][0]|... ^ 分解フェーズ終了時点での操作対象ブロックはここ
メモリの内容を見ると,各ブロックの左から2番目の区画の値が各桁の数値に対応していることが分かります。
分解フェーズが終わったら,プログラムのふたつめの段階:出力フェーズに進みます。既に各桁の計算は終わっているので,操作対象のブロックを左に戻しながら,各ブロックの2番目の区画の値をアスキーコードに変換して(値に48を足して)出力するだけです。ここで,最も左のブロックまで進んだことを判定するために,予めブロックの1番目の区画に格納しておいたインデックスを利用しています。