Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Reid G.C.Thinking in PostScript.1990.pdf
Скачиваний:
17
Добавлен:
23.08.2013
Размер:
846.44 Кб
Скачать

LOOKING INTO DICTIONARIES

Dictionaries contain key–value pairs. In addition to being able to retrieve specific entries from a dictionary with the get or load operator, you can see what entries already exist in the dictionary with the forall operator. This can be useful for various purposes, such as finding out what fonts are currently defined in the interpreter (looking in the FontDirectory or SharedFontDirectory dictionaries) or in debugging, to figure out which dictionary is currently on top of the dictionary stack.

Using the forall Operator

Looking through existing dictionaries can be instructive, even if it isn’t part of any production code you are writing. One of the most useful operators for this purpose is the forall operator. This operator loops through the contents of a dictionary and executes your procedure for each key–value pair found in the dictionary. Example 10.8 shows a simple way to enumerate a dictionary with the forall operator.

Example 10.8: Browsing a Dictionary with forall

/browse-dict

% dictionary browse-dict –

{ %def

 

{ %forall

exch % name first (key: ) print == (val: ) print ==

}forall

}bind def

userdict browse-dict

The entries will not come out in a particularly sensible order. Instead, they are in hash table order, in which the location in the dictionary is determined by deriving an index directly from the key you used. (To digress slightly, an example hash function might be to add up the byte values of all the characters in a name object and use that number as an index into an array.) To make the forall operator a little more useful, you can use the type operator to take a look at each value in the dictionary, and take different measures depending on the data type. This will help to look into other dictionaries and arrays that might be lurking inside the

Chapter 10: USING DICTIONARIES

127

dictionary you are perusing. But be careful if you use a recursive approach, since a dictionary may contain a reference to itself.

Example 10.9 shows another implementation of the browse-dict procedure; this implementation also browses subdictionaries and arrays. It uses the type operator to recognize these composite data types. This is much more complicated-looking code, mostly because of the inelegance of the ifelse constructs in the middle that are needed to check on the types of the data.

Example 10.9: Using type on each Element in a Dictionary

/browse-dict

% dictionary browse-dict -

{ %def

 

 

{ %forall

 

 

exch (key: ) print ==

 

 

dup type

 

 

dup /dicttype eq { %ifelse

 

 

(subdictionary: ) print

 

browse-dict

% recursively browse dictionary

 

}{

 

 

(value: ) print ==

 

 

} ifelse

 

}forall

}bind def

/browse-array

% array browse-array -

{ %def

 

{ %forall

dup type

dup /dicttype eq { %ifelse (subdictionary: ) print

browse-dict % recursively browse dictionary

}{

dup /arraytype eq { %ifelse (array: ) print browse-array

}{ %else

(value: ) print == } ifelse

} ifelse

}forall

}bind def

128

Chapter 10: USING DICTIONARIES

Using the where and known Operators

A very good way to make sure your code is portable if you are using features that may or may not be present on all implementations is to use either the known or the where operators. These operators allow you to check for the existence of names in dictionaries, and execute some conditional code based on whether or not you find them.

For example, let’s say you want to use four-color operations in the CMYK color space (cyan, magenta, yellow, and black). As a point of interest, the reason K is used by convention—rather than B—to represent black is that B is already used to represent the color blue in the RGB color model (red, green, blue).

You want to use the setcmykcolor operator, but if it isn’t available, you can simulate it using setrgbcolor. Using the where operator, you can define a simulation only if the name setcmykcolor is not defined already in the interpreter (see Example 10.10).

Example 10.10: Conditionally Defining a Procedure

% /C points to “setcmykcolor” if it exists, else it emulates it with “setrgbcolor” /C

/setcmykcolor where { %ifelse /setcmykcolor get

}{ %ifelse

{ %def

1 sub 3 { %repeat

3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat

setrgbcolor

} bind

} ifelse

def

In this case the /C is left on the operand stack while where is executed to determine whether or not the simulation is needed for setcmykcolor. If the name setcmykcolor is found, the current definition of it is loaded onto the operand stack. In either case, the name /C will have equivalent functionality to the setcmykcolor operator, and can be used throughout the program with the same arguments that setcmykcolor requires, without having to adjust the rest of the program according to the color model or the interpreter used.

Chapter 10: USING DICTIONARIES

129