PostScript の変数の評価

http://www.hizum.net/diary/?date=20060913#c03 より。手続き A 内で変数を評価するとき、手続きが生成された時点での値を使いたい場合どうすればいいかという話です。長くなったので、コメント欄ではなく、こちらに書きます。
まず、

  • hoge 部分に {} があり、その中に評価したい変数がある場合それが評価されない
GS> /rate 116.78 def
GS> {true{rate div}if} [exch{dup where{exch get}if}forall] cvx
GS> ==
{true {rate div} --if--}
  • hoge 内の fuga がどこかで定義した手続きの場合、これで作られる手続きは、実行しても fuga の部分が実行されず fuga の中身をスタックに積むだけになってしまう
GS> /fuga {foo} def
GS> /bar {fuga} [exch{dup where{exch get}if}forall]cvx def
GS> bar
GS> ==
{foo}

の2つですが、もうちょっと頑張ればどうにかなります。

/executable_array? {
    dup type /arraytype eq
    1 index type /packedarraytype eq
    or
    1 index xcheck
    and
} def
/eval_key{
    [ exch
    {
        executable_array?{
            eval_key
        }{
            dup where{
                exch get
                executable_array?{
                    /exec cvx
                }if
            }if
        }ifelse
    }forall ] cvx
}def

を定義すれば、

GS> /rate 116.78 def
GS> {true{rate div}if} eval_key
GS> ==
{true {116.78 --div--} --if--}
GS> /fuga {(Hello)=} def
GS> /bar {fuga} eval_key def
GS> bar
Hello

うまくいきます。……と思ったけど fuga が再帰していたら、fuga を再定義したとき挙動が変わってしまいますね。
……次行ってみよう

  • hoge の部分で一時的な変数が使いたい時、その時点の辞書スタックで既に定義されている変数名を使ってしまうと評価されてしまう

これは、手続きを生成した時点で、辞書スタックのトップにある辞書で定義されている名前だけを評価すれば、あまり予想外の振る舞いをしないのではないかと思いました。

/eval_currentdict_key{
    [ exch
    {
        % executable_array? は上で定義したもの
        executable_array?{
            eval_currentdict_key
        }{
            currentdict 1 index known{
                load
                executable_array?{
                    /exec cvx
                }if
            }if
        }ifelse
    }forall ] cvx
}def

と定義すると、

/rate 0.5 def
/gen_yen_to_dollar{
    0 dict begin
    /rate 116.78 def
    /hoge{(大金だ!)=}def
    {
        1 dict begin
        /a4 exch def
        a4 10000 gt{
            hoge
            a4 rate div
        }{
            a4 rate div
        }ifelse
        end
    }eval_currentdict_key
    /rate 2.0 def
    end
}def
/rate 3.14 def
/yen_to_dollar gen_yen_to_dollar def

とした時、

GS> 980 yen_to_dollar =
8.39185
GS> 20000 yen_to_dollar =
大金だ!
171.262

のようになります。(この場合も再帰があると再定義で挙動が変わってきます。)
ここまで来ると、元の OCaml の単一代入からはかなり遠ざかっているような気がします。(代入は 1 回だけという制限があれば、元の記事の closure で済んでしまうのだな)