PostScript でチャーチ数(が書けてない)

前フリ

MixiC/C++ の課題丸投げコミュで,ポストスクリプトで〜せよ.C言語です.
とか書いてあって,あまりレスされてなかったので,
PostScript の勉強を兼ねて書き込んでみた.
PostScript で,円弧(arc)や線分(lineto).塗り潰し(fill)をするのは簡単なので,
比較的スムーズに課題には答えることができたが,
PostScript の名前空間(辞書,dict) がよくわからなかったので,
ちょっと探して,わかったのことをまとめておくことにする.

というか,PostScript でチャーチ数を書きたかっただけです.すみません.

前提知識

  • PostScript は,プログラミング言語である.
  • PostScript は,逆ポーランド記法で記述し,スタック指向で処理される.
  • PostScript の処理系は,GhostScript (gs, gsnd).
  • PostScript のコメントは,% から行末まで.
  • トークンには,データと実行可能なものがある.
  • [ 1 2 3 ] で配列
  • { 1 2 3 } で,処理をまとめる.
  • シグネチャが,「引数 関数名 スタックに返す値 」のようになっている
    • add 関数だと,「num1 num2 add sum 」で,add という関数名で2引数をポップして,スタックに sum を返す感じ.

逆ポーランド記法で〜の辺は,こんな感じ.
print(10 + 20 * 30) を表現しようとすると,こんな感じになる.

10 20 30 mul add ==

擬似的に実行してみると.

  • 1. まず実行可能じゃない 3つを積む.stack => (top) 30 20 10 (bottom), com => (mul add ==)
  • 2. mul が来たので,スタックトップから2つ消費して,計算結果をまた積む.stack => (top) 60 10 (bottom), com => (add ==)
  • 3. add が来たので,同様に,stack => (top) -590 (bottom), com => (==)
  • 4. == が来たので,スタックから1つ消費して,-590 を表示.stack => (top) (bottom), com => ()

gs は,インタラクティブモードで PostScript ファイルを表示しようとするけど,
gsnd は,NoDisplay の略らしく,
バッチなら gsnd -q -dBATCH .ps
インタラクティブなら, gsnd -q を基本とする.

で,コピペ.(コメントは筆者)

/closure{
    [
        countdictstack array dictstack
        [
            exch{
                dup systemdict eq{
                    cleartomark mark systemdict
                }if
            }forall
        ]
        {
            begin
        }/forall cvx               % ここまでで名前空間を実行可能オブジェクトに保持
        counttomark 2 add -1 roll  % スタックからクロージャの中身を挿入して保持
        /stopped cvx
        4 index length{
            end
        }/repeat cvx
        {
            stop
        }/if cvx                   % エラー処理
    ]cvx
}def

closure は,参考のところからコピーしたので,
内容を読んでみる.
以下は,bluebook からコピペした,シグネチャ
関数名は,()括弧で囲んである.

  • - (countdictstack) int : count elements on dict stack
  • int (array) array : create array of length int
  • array (dictstack) subarray : copy dict stack into array
  • array proc (forall) - : execute proc for each element of array
  • bool proc (if) - : execute proc if bool is true
  • mark obj_1..obj_n (cleartomark) - : discard elements down through mark
  • any (dup) any any : duplicate top element
  • a_{n-1}..a_0 n j (roll) a_{j-1 mod n}..a_0 a_{n-1}..a_{j mod n} : roll n elements up j times
  • - (stop) - : terminate stopped context
  • any (stopped) bool : establish context for catching stop
  • any_n..any_0 n (index) any_n..any_0 any_n : duplicate arbitrary element
  • array (length) int : number of elements in array
  • int proc (repeat) - : execute proc int times
  • any (cvx) any : make object be executable

補足

  • stop / stopped は,throw / catch 関係のようなもの,stopped ブロック内で stop すれば,true を返す.
  • cvx がeval みたいなもの.

で,書いてみたんだけど,two がうまくいかない.
ふーむ.何か勘違いしてるのかなぁ?

% zero(f)(x) => x
/zero {
    1 dict begin
    /f exch def
    /x exch def
    x
    end
} closure def

% n + 1 => f(n(f)(x))
/inc_cn {
    1 dict begin
    /n exch def

    {	
        1 dict begin
        /f exch def
        /x exch def

        x /f cvx n f
        end
    } closure
    end
} def

% m + n => n(f)(m(f)(x))
/add_cn {
    1 dict begin
    /n exch def
    /m exch def
    {
        1 dict begin
        /f exch def
        /x exch def
        x /f cvx m /f cvx n
        end
    } closure
    end
} def

/one /zero cvx inc_cn def
%/two /one cvx inc_cn def
/two /one cvx /one cvx add_cn def

(: print zero) ==
0 { 1 add } zero ==
(: print one) ==
0 { 1 add } one ==
(: print two) ==
%0 { 1 add } two ==

参考