BefungeでFizzBuzz[追記あり]


(※より短く、密度を濃くしたソースコードを追記しました。)
二次元指向言語BefungeでFizzBuzzを書いたのでコードを紹介します。
始めて24時間くらいなのでコードは稚拙ですし最適化されていませんが、完成はしたので…

仕様

  • 100までFizzBuzzする(もっといけるけどとりあえずで100)
  • 区切りにはLF(改行)を使う

全体像

Befunge
0 > 1+:3%!#v_:5%!#v_v
  ^,+<     "        :
 > 55^     z        .
 |!`+9+9<  z        :
 @  >99*^  i
    ^:,,,, F"Buzz"<   <
    ^      "#       <
           >,,,,:96+%!|
    ^               : <

こうして見ると「卍」みたいですね

ループ処理部分

Befunge
0 > 1+
  ^,+<
 > 55^
 |!`+9+9<
 @  >99*^

いろいろくっついてますが、大事なのはこの抜粋した部分です。
一番最初にポインタが出現する左上でスタックの一番下に0を入れ、そのあとfor文に掛けています。1+というのは一番下のカウンタに使用するスタックの値を1増やすって意味ですね。
そのあとループ中処理を終わらせたポインタは、どのルートを通っても最終的には99*^の左の>にたどり着き、その後9*9+9+9の式でスタックに99を格納します。1そして、その99をスタックの一番下にあるカウンタと比較し、カウンタが99以下だったら上、99より大きかったら下に行きます。下にはプログラム終了を示す@が置いてあるので下に行ったらプログラムが止まり、上に行くと55+,という改行を出力するコードを踏んだ後また1+に戻ります。
これを繰り返すことで、ループを運用しているわけです。

選り分け部分

Befunge
3%!#v_:5%!#v_v

この部分では、FizzBuzzの肝と言っていい選り分けを行っています。
まず:3%、カウンタ(正確にはそのコピー)を3で割った余りを算出して、それを!#v_で「余りが0なら下へ、でなければ右へ」行かせています。その隣の:5%!#v_vも仕組みとしては大差なくて、違う点はどちらの道を辿ろうと下に行っていることくらいです。
ちなみにこの選り分け部分では、値は次の三つのカテゴリに分けられます。

  • 「3で割り切れる数」
  • 「5で割り切れるが3で割り切れない数」
  • 「5でも3でも割り切れない数」

「5でも3でも割り切れる数がないじゃねーか頭バグってんのか」と思ったそこのあなた、大丈夫です。それは「3で割り切れる数」の処理終了後にやりますから。

出力部分

Begunge
+<     "        :
5^     z        .
+9+9<  z        :
>99*^  i
^:,,,, F"Buzz"<   <
^      "#       <
       >,,,,:96+%!|
^               : <

基本的には文字列を"リテラルで読み込んで,で出力しているだけです。
特筆すべき点としては、さっきの選り分けで「3で割り切れる数」として判定された値にFizzを付加したあとの挙動でしょうか。:96+%部分でその値が96+、つまり15で割れるかどうか判定して|で分岐させ、割り切れる場合は丁度近くにある「5で割り切れるが3で割り切れない数」用のBuzz付加ラインに送り込んでいます。15で割り切れなかった場合はループ終了地点に放り込んでいます。

インタプリタで実行してみた

Befungeをオンライン実行できる有能インタプリタ「Befunge Playground」で実行してみました。

これぞBefunge、って感じでポインタが動いてますね。じっと見ていたくなります。

総括

もうちょっと面積と文字数減らせたんじゃないの?

追記

この記事を書いた後にトライしてみたら割と短縮できたので載せておきます。解説はしません。
しめて横幅2文字・行数2行・文字数42文字の短縮に成功しました。

Befunge
0>1+:3%!#v_:5%! #v_ v
>^       >"zziF"vv <
^,+55_@v<v      <  |:
>v   !v,,,"Buzz" <  .
^ :, ` ,^< ,       #
 >"c"^,>:96+%!     ^
^    :<       <    <<

ちなみに実行結果はこんな感じ

追記2

更に改良しました。

Befunge
0>1+:3%!#v_:5%!#v_v
>^> "z" #<"i"v  v<
^,+55_@v<v"F"<   |:
     !v,,,"Buzz"< .
  :, ` ,^< ,     #
> "c"^,>:96+%!   ^
^    :<          <<

Fizzの出力方法をちょっと変えるだけで幅が2文字分減りました。

追記3

まだ改良できました。

Befunge
0>1+:3%!#v_:5%!#v_v
>^> "z" #<"i"v    #
^,+55_@vv,"F"<  v_^
v  ,,!#<,,"Buzz"< :
  :, `v,<     >  ^.
>:"c"^>,:96+%!^   >

1行減りました。
ループ処理部への移動をちょっと工夫して気合で詰めてます。

追記4

更なる改良を加えました。

Befunge

0>1+:3%!#v_:5%v> v
>^> "z" #<"i"v>| #
^,+55_@vv,"F"< v_^
v,,!#!#,,"Buzz"< :
 ,   `v,<     > ^.
>:"c"^>,:96+%!^  >

これ以上は無理


  1. 実のところASCIIコード99番の「c」を"リテラルで読み込ませた方が文字数が少なく済むことに今気付きました