Common LispでFizzBuzzを解いてみた

Ruby FizzBuzz最短コードメモ (51bytes) ネタバレ注意 - Qiitaを見て、Common Lispでは何byteで書けるかな?と思ったのでやってみた。
結論から言うと、あまり短いコードは書けなかった(技量的に)。しかしながら、面白いことを2つ発見したのでまとめておく。


負の剰余

Cで配列のインデックスを指定する場合や素数判定のプログラムを書くときに剰余演算子を用いたことはあった。そのときは正の剰余を想定して書いたので、負の剰余のことなぞ考えたこともなかった。しかし、上記の記事では負の剰余を用いており、ではCommon Lispではどうなるのかな?と思ったのでまとめてみた。処理系はCCL1.6。

n (mod n 15) (mod n -15) (mod -n 15) (mod -n -15)
1 1 -14 14 -1
2 2 -13 13 -2
3 3 -12 12 -3
4 4 -11 11 -4
5 5 -10 10 -5

rubyと同じようだ。他の言語では、どうなるだろう?

FizzBuzz

FizzBuzz問題を最短コードで書く場合"FizzBuzz"から、3の倍数なら"Fizz"を、5の倍数なら"Buzz"を, 15の倍数なら"FizzBuzz"を取り出すのが良いのではないかと考えた。そして、取り出すための関数として"subseq"を選んだ。すると以下のようになる。

(subseq "FizzBuzz" 0 4) => "Fizz"
(subseq "FizzBuzz" 4 8) => "Buzz"
(subseq "FizzBuzz" 0 8) => "FizzBuzz"

これを任意のnを与えてできないかと考えていたら、妙な数式が出来上がった。

(subseq "FizzBuzz" start end) 
start: (mod (mod (expt n 4) 15) 9)        ; exp(n,4) % 15 % 9 
end  : (+ (mod (mod (expt n 4) 15) -5) 8) ; exp(n,4) % 15 % -5 + 8

これを使うと、以下の様になる。

n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
start 4 4 0 4 4 0 4 4 0 4 4 0 4 4 0
end 4 4 4 4 8 4 4 4 4 8 4 4 4 4 8
sebseq "" "" "Fizz" "" "Buzz" "Fizz" "" "" "Fizz" "Buzz" "" "Fizz" "" "" "FizzBuzz"

ほぼ数字遊びのような感じだが、うまく動くので何らかの数学的要因があるかもしれない。
しかも、これが面白いのは、3でも5でも15の倍数でもない値のときは""となるところだ。
だが、これをいざコードにすると、計算式が長い。また、3でも5でも15の倍数でもない値のとき、その数を返すわけではないのではないので、その値を表示させるには、さらに一工夫必要で、結果としてコードが長くなる。残念。


当初の目的であった、Common Lispで最短でFizzBuzzを解くというのは達成できなかったが、なかなか面白い発見はできた。Common Lispで最短FizzBuzzはひとまず止めるが、Common Lispの技量が上がったらまた解いてみようかな。また、新しい言語の取得の一環として最短でFizzBuzzを解くというのは、良い訓練ではないかと思った。