Dictionaries
In the previous chapter on procedures you were asked to use variables in the exercises. If one isn't careful, one could encounter the problem of aliasing.
Take a careful look at the following code and try to predict what the resulting image looks like:
/height 595 def
/width 419 def
/box { % x y width height
/height exch def
/width exch def
/y exch def
/x exch def
x y moveto
x width add y lineto
x width add y height add lineto
x y height add lineto
x y lineto
} def
newpath
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
stroke
showpage
You will recognize a few sections in the above code
Definitions
/height 595 def
/width 419 def
Here we bind the keys height
and width
to the values 595
and 419
.
These are the dimension of A5 paper in PostScript points, the default scale that is used.
Procedures
/box { % x y width height
/height exch def
/width exch def
/y exch def
/x exch def
x y moveto
x width add y lineto
x width add y height add lineto
x y height add lineto
x y lineto
} def
The code goes on to bind a procedure to the key box
.
The procedure uses the tip from the variable chapter to bind the arguments to names.
Note that if we write the arguments in order
x y width height
height
ends up on top of the stack. That is why you have to bind them in the reverse order.
It goes on to draw a box by moving to the lower left corner, and drawing lines to each corner in a counter-clockwise fashion.
x y moveto
x width add y lineto
x width add y height add lineto
x y height add lineto
x y lineto
Drawing
The next section of code uses the defined box
procedure to draw, and stroke, three boxes.
newpath
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
stroke
The same arguments are passed each time. So one would expect to see only one box.
Aliasing
As hinted in the opening paragraph the unexpected boxes originate from aliasing. If you look carefully you will notice that the "global" width
and height
variables are reassigned each time the box
procedure is called.
One could solve the problem if one could scope the variables to the procedure only. This is where dictionaries come in.
Use of dictionary
The above image is the one we expected. It is drawn by the following program.
/height 595 def
/width 419 def
/box { % x y width height
4 dict begin
/height exch def
/width exch def
/y exch def
/x exch def
x y moveto
x width add y lineto
x width add y height add lineto
x y height add lineto
x y lineto
end
} def
newpath
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
0 height 2 div 72 height 3 div box
stroke
showpage
The only difference with the program mentioned in the beginning of this chapter is the pair
4 dict begin
and
end
4 dict
creates a dictionary that can contain 4 items and puts it on the operand stack, begin
takes the dictionary from the stack and makes it the current dictionary, so that it will be used to bind variables in and look up names from.
end
restores the previous dictionary as the current dictionary.
Dictionary Stack
The operand stack is of central importance in any PostScript program. But it is not the only stack that PostScript knows about.
There is also a dictionary stack. Just like the operand stack the dictionary stack is a stack. We saw that you can use begin
and end
to push dictionaries onto and pop them from the stack.
The dictionary stack plays an integral role in PostScript programs. Specifically how it looks up values bound to names. When the PostScript interpreter encounters a name like bergen
it will search for a binding in the current, or top-most, dictionary.
If it finds the key in the dictionary it returns the value bound to it.
If it can not find the name in the dictionary it begins searching in lower dictionaries on the dictionary stack.
Interestingly enough, PostScript programs start with a number of dictionaries on the stack. The most important one is the system dict. It contains all definitions of the PostScript operators!
Literal Dictionaries
Creating a dictionary with the dict
operator and pushing it on the dictionary stack with begin
makes it the current dictionary. It will be used to bind names to values with the def
operator.
There is a literal notation for dictionaries. For example, the following snippet
3 dict begin
/foo 1 def
/bar 2 def
/baz 3 def
...
is equivalent to
<</foo 1 /bar 2 /baz 3>> begin
...
Exercises
- What values are left on the stack after the following program executes.
<</a 1 /b 2>> begin
/a 3 def
a b add
/b exch def
a b
end
- In the following snippet, what value is the name
a
bound to, according to the PostScript interpreter.
3 dict begin
/a 1 def
/b 2 def
/c <</a 3 /b 4>> def
c begin
...