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