- •Preface
- •DESIGN FEATURES
- •STRUCTURED PROGRAMMING TECHNIQUES
- •PROGRAMMING TASKS
- •WINDOW SYSTEMS, COMMUNICATIONS, AND DISPLAYS
- •DATA STRUCTURES AND ALGORITHMS
- •CONCLUDING THOUGHTS
- •PostScript is Not Like C
- •COMPARISON OF LANGUAGE MECHANISMS
- •EXPRESSING AN ALGORITHM AS A PROGRAM
- •THE UNIX SHELL AND OPERATING SYSTEM
- •INPUT, OUTPUT, AND THROUGHPUT
- •CONCLUDING THOUGHTS
- •Foundations
- •POSTSCRIPT LANGUAGE SYNTAX
- •SIMPLE PROGRAM STRUCTURE
- •Make Definitions First
- •Indentation Style
- •SETTING UP TEMPLATES
- •DECLARING AND USING VARIABLES
- •Arithmetic with Numeric Variables
- •Using the // Notation for Constants
- •ALLOCATING MEMORY
- •GETTING MEMORY BACK
- •OPENING AND CLOSING FILES
- •COMPARISONS AND EQUALITY OF OBJECTS
- •CONCLUDING THOUGHTS
- •Some Typical Programs
- •A TYPICAL PAGE DESCRIPTION PROGRAM
- •FONT PROGRAMS
- •PROGRAMS THAT READ DATA
- •QUERY PROGRAMS
- •ENCAPSULATED POSTSCRIPT PROGRAMS
- •PERSISTENTLY RESIDENT PROGRAMS
- •CONCLUDING THOUGHTS
- •Understanding the Stack
- •A QUICK OVERVIEW OF DATA TYPES
- •NAME LOOKUP
- •HOW OPERATORS USE THE STACK
- •GROUPING AND VISUAL CHUNKING
- •THINKING BACKWARD AND SIDEWAYS
- •COMPOSITE OBJECTS
- •THE OTHER STACKS
- •The Dictionary Stack
- •The Execution Stack
- •The Graphics State Stack
- •CONCLUDING THOUGHTS
- •Trusting the Stack
- •SAFETY OF DATA ON THE STACK
- •WHERE ARE THE DATA GOING?
- •REARRANGING THE STACK
- •Using the dup and index Operators
- •Using the roll Operator
- •CONDITIONALS AND LOOPS
- •RECURSION AND LOCAL VARIABLES
- •CONCLUDING THOUGHTS
- •Building Conditional Statements
- •SIMPLE CONDITIONALS
- •SETTING UP THE CONDITION
- •CONDITIONALS ARE NOT MAGIC
- •NESTED CONDITIONALS AND ELSE CLAUSES
- •COMPOUND CONDITIONALS
- •CONCLUDING THOUGHTS
- •Using Looping Constructs
- •LOOP BASICS
- •USING THE LOOP INDEX
- •LOOPS ARE PROCEDURE BODIES
- •LOOPS OF INSTRUCTIONS
- •EXITING LOOPS PREMATURELY
- •CONCLUDING THOUGHTS
- •Procedures
- •WHAT EXACTLY IS A PROCEDURE?
- •PARAMETER PASSING
- •CONSTRUCTING GOOD PROCEDURES
- •What to Name Your Procedure
- •A Useful Naming Convention
- •SELF-MODIFYING PROCEDURES
- •CONCLUDING THOUGHTS
- •Using Dictionaries
- •DICTIONARIES FOR NAME SCOPING
- •LOCAL DICTIONARIES
- •GLOBAL DICTIONARIES OF PROCEDURES
- •MAINTAINING THE DICTIONARY STACK
- •INTO AND OUT OF DICTIONARIES
- •LOOKING INTO DICTIONARIES
- •Using the forall Operator
- •Using the where and known Operators
- •REDEFINING OPERATORS
- •Changing the Behavior of Operators
- •Debugging with Redefined Names
- •Proper Nesting of Redefinitions
- •CONCLUDING THOUGHTS
- •Creating and Manipulating Data
- •CONSTRUCTING AN ARRAY
- •CONSTRUCTING A STRING
- •MANIPULATING DATA WITH PUT AND GET
- •CONCATENATING ARRAYS AND STRINGS
- •INPUT AND OUTPUT OF STRING DATA
- •ARRAYS VERSUS DICTIONARIES
- •ADVANCED TECHNIQUES
- •CONCLUDING THOUGHTS
- •Storing and Using Data
- •Data and the Operand Stack
- •Data and Algorithms for Underlining
- •CLASSICAL DATA STRUCTURES
- •Linked Lists
- •Using Arrays to Form Lists
- •Using Dictionaries to Form Lists
- •Queues, Trees, and Other Data Structures
- •CONCLUDING THOUGHTS
- •Program Data and Instructions
- •TURNING DATA INTO INSTRUCTIONS
- •TURNING INSTRUCTIONS INTO DATA
- •DATA CONVERSIONS
- •CONCLUDING THOUGHTS
- •File Objects
- •Streams and Files
- •PostScript File Operators
- •OPENING AND CLOSING FILES
- •READING AND WRITING FILES
- •Reading from a File
- •Writing to a File
- •Copying and Renaming Files
- •WRITING FORMATTED DATA TO FILES
- •Writing Out Various Data Types
- •Spaces, Tabs, Returns, and Special Characters
- •FILE STATUS INFORMATION
- •RANDOM VERSUS SEQUENTIAL ACCESS
- •CONCLUDING THOUGHTS
- •Appendix
- •Answers to Exercises
LOOPS ARE PROCEDURE BODIES
Since the proc passed to all of the looping operators is just an ordinary procedure body, you can perform any of the standard operations on it. One often-overlooked but very important detail is to apply bind to the procedure body before executing the loop instruction, so that the loop will execute more quickly (see Example 8.5). Be careful, however, not to apply bind to a loop that is inside a procedure body that has also been bound; it doesn’t accomplish anything further (since bind applies to all nested procedure bodies recursively) and may degrade performance slightly if bind is executed each time the procedure is called.
Example 8.5: Using bind on Loop Bodies
0 1 1000 { %for
0 moveto 0 400 rlineto stroke } bind for
You can also take advantage of the fact that a loop body is an array (all procedures are arrays) and actually put objects directly into it to save time. Example 8.6 shows this technique applied to creating an Encoding array for a font, where you have to supply the array object itself 255 times during the execution of the loop. This saves the interpreter name lookup overhead each time around the loop.
Example 8.6: Putting Objects into Loop Bodies
/encoding currentfont /Encoding get def 0 1 255 { %for
ENCODING_HERE exch /.notdef put } dup 0 encoding put bind for
This example is a bit tricky, but it has a simple underlying concept. The trick is to put a dummy object into the procedure body (in this case, the executable name ENCODING_HERE, but it could be any object), then, before the procedure is ever executed, to replace that dummy object with the actual encoding array itself, instead of a name. The advantage to this approach is that you don’t have to look up the name all 256 times around the loop; the array object itself is in the procedure body, which saves you 256 name lookups and a lot of time.
Chapter 8: USING LOOPING CONSTRUCTS |
99 |
Even though image, colorimage and imagemask are not technically considered looping operators, they may in fact be executed many, many times. This makes them qualify as looping operators to a large degree, and the techniques just discussed also apply to these operators.
In the most standard situation where you supply the image data in-line in your input file and read from currentfile, the size of the buffer for readhexstring is typically only as big as one scan line of the image. This means that the data acquisition procedure for image or imagemask will be executed once for each scan line in the image, which may result in the procedure’s being executed hundreds of times—that certainly qualifies it as a looping operation.
The technique just illustrated in Example 8.5 of using bind on the procedure body is a very good idea for images, and the idea presented in Example 8.6 can also be applied to both currentfile and the string buffer for readhexstring, as shown in Example 8.7. However, a slightly different approach will be used to construct the image procedure (other than using put), to make it a little bit more readable.
Example 8.7: Optimizing the Image Procedure
/currfile currentfile def |
%get the current file object |
/buffer 128 string def |
% image buffer |
128 400 1 [ 1 0 0 -1 0 400 ] |
|
[ currentfile buffer /readhexstring cvx /pop cvx ] cvx bind image
% hex data goes here
The procedure in this call to image now contains four objects, none of which are executable names, thanks to bind and the way we constructed the procedure. (The procedure contains a file object, a string object, and two operator objects.)
If you’re having difficulty following the way the procedure body was constructed in Example 8.7, you might skip ahead and read Constructing an Array in Chapter 11.
100 |
Chapter 8: USING LOOPING CONSTRUCTS |
LOOPS OF INSTRUCTIONS
One very powerful use of looping operators is to perform repeated instructions, such as a series of lineto or curveto operators. This can be particularly effective in a printer driver or any situation in which you might want to avoid repeating the names of the operators many times. Example 8.8 has a large number of repetitive instructions; the program sets several lines of text sequentially down the page. It can be written to minimize the number of individual instructions issued, as seen in Example 8.9.
Example 8.8: A Program with Repeated Instructions
72 750 moveto
/Times-Roman findfont 10 scalefont setfont gsave
(Sometimes you have to construct a loop that may run for an indeterminate) show grestore
0 -12 rmoveto gsave
(length of time. For example, you might loop until an EOF condition is) show grestore
0 -12 rmoveto gsave
(met, or until no more spaces are found in a string, etc.) show grestore
0 -12 rmoveto
Example 8.9: Using repeat for Instructions
72 750 moveto
/Times-Roman findfont 10 scalefont setfont
(met, or until no more spaces are found in a string, etc.)
(length of time. For example, you might loop until an EOF condition is) (Sometimes you have to construct a loop that may run for an indeterminate) 3 { %repeat
gsave show grestore 0 -12 rmoveto } bind repeat
Chapter 8: USING LOOPING CONSTRUCTS |
101 |
Figure 8.4: Output of Example 8.9
output page
Sometimes you have to construct a loop that may run for an indeterminate length of time. For example, you might loop until an EOF condition is met, or until no more spaces are found in a string, etc.
EXITING LOOPS PREMATURELY
Sometimes you have to construct a loop that may run for an indeterminate amount of time. For example, you might loop until an end-of-file condition is met, or until no more spaces are found in a string. The best way to construct a loop of this type is to use the loop and exit operators. The exit operator will simply cause the innermost looping context to be broken, allowing exit from the loop at any point.
Here is the basic loop construct:
{ %loop
exit_condition { exit } if
} loop
The exit_condition is some test that decides whether or not it is time to exit the loop. For example, in the program in Example 8.10, the exit condition is provided by the readline operator, which returns false if the end of the file is reached, true otherwise.
102 |
Chapter 8: USING LOOPING CONSTRUCTS |