掴んだ物(技術)はちゃんと食べる記録帳

興味を持ったものに対する足跡を残していくために作成しました。勉強しながら書いているので、間違いや短絡もあると思います。ご指摘いただけると喜びます。

Let's PostScript(9) 辞書Objectとカレント辞書

おきて破りの一日2記事です(笑)

辞書Object

辞書Objectは配列Objectと非常に似ていますが、データの管理の仕方が異なっています。 配列Objectの場合、データの管理はデータの配置順(Index)なのに対し、辞書ObjectはKeyとValueの組み合わせで管理します。

そのため、辞書Objectの作成は以下のように指定します。KeyとValueをペアで登録していく形ですね。 << key1 Value1 .... keyn valuen >>

これは実際のスクリプトを見るのが早いと思います。

GS><< /data1 123 /data2 (abcd) /data3 1.25>>
GS<1>/data2 get
GS<1>=
abcd
GS>

先ほどの配列と極めてよく似ていることがわかると思います。異なるのは配列ObjectがindexでValueを取得したのに対して、辞書ObjectはkeyでValueを取得しに行っているという点です。

仮想メモリの使われ方も前回の配列Objectと基本同様であると思います。

辞書Stack

PostScriptのインタープリタは辞書Objectを使用して、オペレータの検出をしています。インタープリタが参照する辞書Objectは階層化(辞書Stack)されていて、上から順に検索をしていきます。

PostScript起動時に辞書Stackに積まれている辞書Objectは

  • システム辞書:オペレーターの名前と処理が格納されている辞書
  • グローバル辞書:グローバル仮想メモリを使用する、辞書オブジェクト
  • ユーザー辞書:ローカル仮想メモリを使用する、辞書オブジェクト

の3種類です。ユーザー辞書、グローバル辞書、システム辞書の順で積まれています。そして、一番上に積まれている辞書Objectのことをカレント辞書と呼びます。 そしてカレント辞書用のオペレーターで使用頻度が高いものの一つがdefオペレータです。 defオペレータは、カレント辞書にkeyとValueのペアを登録します。辞書Stackに積まれた辞書はインタプリタが自動的に取得するため、OperandStackに頼らない柔軟なデータや手続きの制御ができるようになります。

カレント辞書への登録

以下に一例を示します。カレント辞書にtmpを登録、再登録することで、あたかも変数のように使っていることがわかるでしょうか。

GS>/tmp 0 def
GS>5 {/tmp tmp 1 add def} repeat
GS>tmp
GS<1>=
5

手続きObjectを登録することで関数のように扱うこともできます。

GS>/average {add 2 div} def
GS>100 20 average
GS<1>=
60.0

カレント辞書の切り替え

ここまでの例では、カレント辞書はユーザー辞書を使用していましたが、スクリプトが複雑化するに従いユーザー辞書では制御が難しくなります。

そのため、一般的には処理の塊ごとに専用のカレント辞書を用意し、処理の中のみその辞書を使用し、処理の完了後はカレント辞書を破棄するのが一般的です。

以下のような実装となります。

GS>/temp 1234 def
GS>10 dict
GS<1>begin
GS>/temp (abcd) def
GS>temp
GS<1>=
abcd
GS>end
GS>temp
GS<1>=
1234
GS>

最初の

GS>/temp 1234 def

はカレント辞書がユーザー辞書で、ユーザー辞書に対し、tempを1234で登録します。

次のコードで、10要素サポートする辞書を新たに作成し、beginオペレータで辞書Stackに作成した辞書をPushします。 それにより、カレント辞書が新たに作成した辞書に切り替わります。

GS>10 dict
GS<1>begin

この状態でtempをインタープリタが処理すると、カレント辞書である新たに作成した辞書のtempから定義(abcd)を検出します。

GS>/temp (abcd) def
GS>temp
GS<1>=
abcd

endオペレータでカレント辞書を辞書StackからPopします。それにより、カレント辞書がユーザー辞書に切り替わります。

GS>end

この状態でtempをインタープリタが処理すると、カレント辞書であるユーザー辞書のtempから定義1234を検出します。

GS>temp
GS<1>=
1234

このように、辞書スタックを操作することにより、定義の有効期間を制御することができるようになります。 これにより、処理の独立性を維持しやすくなります。