FizzBazz

PostScript で http://karetta.jp/book-node/gauche-hacks/023107 っぽいやり方で書いてみる。むしろ、Unix コマンドをパイプで繋げていくのに似ているような。

/map{[3 1 roll forall]}def % map は簡単に自作できます
/integer?{type/integertype eq}def
[1 1 100{}for]
{dup integer?{dup 15 mod 0 eq{pop(FizzBuzz)}if}if}map
{dup integer?{dup 5 mod 0 eq{pop(Buzz)}if}if}map
{dup integer?{dup 3 mod 0 eq{pop(Fizz)}if}if}map
{=}forall

似たようなパターンはまとめるべきなんじゃないかと思ったので、こうする。

% EUC-JP で保存してね
/の倍数なら{
    % any1 integer2 -- boolean
    % any1 が integer2 の倍数であれば true、
    % そうでなければ false をスタックに置く
    % any1 が整数でない場合も false
    1 index type/integertype eq{
        mod 0 eq
    }{
        pop pop false
    }ifelse
}def
/置換{
    % array1 proc any -- array2
    % 配列の各要素について、それを proc に渡した時 true を返すのであれば
    % any に置き換える
    2 dict begin
    /..x exch def
    /..proc exch def
    [exch{dup ..proc{pop ..x}if}forall]
    end
}def
[1 1 100{}for]
{15 の倍数なら} (FizzBuzz) 置換
{5 の倍数なら} (Buzz) 置換
{3 の倍数なら} (Fizz) 置換
{=}forall

15 を直接書くのはいかがなものかと思ったら、そこんところを

{dup 3 の倍数なら exch 5 の倍数なら and} (FizzBuzz) 置換

と書き換えたり

/最小公倍数{
    % integer1 integer2 -- integer3
    % integer1 と integer2 の最小公倍数を計算
    % ユークリッドの互除法を使用
    2 copy
    {
        dup 0 eq{exit}if
        exch 1 index mod
    }loop
    pop idiv mul
}def

を定義して

{3 5 最小公倍数 の倍数なら} (FizzBuzz) 置換

と書き換えたりする。毎回最小公倍数を計算すると遅くなるんじゃないかと思ったら、適当に変数にでも代入する。

でもいちばん PostScript らしいのは

1 = 2 = (Fizz) = 4 = (Buzz) = (Fizz) = 7 = 8 = (Fizz) = (Buzz) =
% 以下略

というスクリプトではないだろうか。機械生成した PostScript はだいたいこんな感じで書かれているような気がする。