- •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
CONSTRUCTING GOOD PROCEDURES
Simplicity, readability, efficiency, and correctness are the most important aspects of your PostScript procedure, although not necessarily in that order. The overall size of a procedure body should be maintained as small as possible, but it is not more efficient to break a big procedure up into smaller ones to call by name from within the larger one, due to the additional name lookup and overhead on the execution stack.
What to Name Your Procedure
Assuming that you will create a procedure to be used by the rest of your program, you will typically give it a name and invoke it by that name. Of course, as with any programming language, the name should reflect the functionality of the procedure as much as possible. Select poor names for your smaller procedures and you may in fact make the program much less readable.
In choosing a name, it is important to consider just how the procedure will be used. If the procedure will be used in the “script” of a document and be invoked hundreds of times, its name should be short, to save space and time in the execution of the program. However, if the procedure is called only from other procedures that you have defined, you gain very little by giving it a short name, since the name is represented by a name object of fixed size once the procedure body has been constructed.
Let’s consider some examples. Can you guess what the procedure called p does in Example 9.8?
Example 9.8: Disorganized and Inefficient Use of Procedures
/b { bind def } bind def /d { def } b /x { exch } b
/m { moveto } b /r { rlineto } b
/c { closepath } b /f { fill } b
/p { %def
/#4 x d /#3 x d /#2 x d /#1 x d
#3 #4 m #1 0 r 0 #2 r #1 neg 0 r c f
} b
100 100 400 50 p
Chapter 9: PROCEDURES |
111 |
It is difficult to decipher all the short names for operators given in Example 9.8, and the function of the program segment becomes less clear as a result. Furthermore, the program is very inefficient, since each of the short names actually invokes a procedure call (and therefore requires a fair amount of overhead).
The procedure in Example 9.9 does the same thing as the one in Example 9.8 but it takes much less time, is more readable, and occupies less memory.
Example 9.9: Efficient Procedure
/rectfill |
% Xloc Yloc Width Height rectfill |
{ %def |
|
/Height exch def /Width exch def /Yloc exch def /Xloc exch def Xloc Yloc moveto Width 0 rlineto
0 Height rlineto Width neg 0 rlineto closepath fill
} bind def
400 50 100 100 rectfill
Also, for comparison, Example 9.10 presents the same program without any local names defined for the arguments:
Example 9.10: Efficient Procedure without Local Names
/rectfill |
% Xloc Yloc Width Height rectfill |
{ %def |
|
4 -2 roll moveto |
|
1 index 0 rlineto |
|
0 exch rlineto |
|
neg 0 rlineto |
|
closepath fill |
|
} bind def |
|
400 50 100 100 rectfill |
|
The purpose of this procedure is much more clear now, since the native PostScript operators can be seen easily, and since the names chosen for
112 |
Chapter 9: PROCEDURES |
the procedure and its arguments are intuitive and make sense. The original example, even though it used local names for all of the arguments, was extremely difficult to understand, mostly because the names were poorly chosen. The last example, without any local names at all, is much easier to follow thanks to the native PostScript operator names. Example 9.9 is probably the easiest of all to understand. Interestingly enough, the only way it differs from Example 9.8 is in the names chosen (and in the indentation style).
Some of the very most difficult programs to understand are those that define hundreds of two-letter names for procedures and have each procedure call three others, making it almost impossible to trace the execution of the program, to cut pieces from it, to maintain it, or to understand it.
A Useful Naming Convention
To cement the ideas behind good naming of procedures and variables, following is an example of a naming convention that can help keep it all straight. You may adopt your own naming conventions, of course.
•Use lower-case letters only for procedure names, since they behave more or less like the PostScript built-in operators, which are all lower-case.
•Use mixed-case for variables, like /LeftMargin or /FontSize.
•If the procedure names are never transmitted in quantity (as they might be in the body of a document), there is no reason to make them short. The interpreter will make them into tokens anyway.
•Use single-letter names for the most commonly called procedures in the body (or script) of a document, keeping them as mnemonic as possible. For example, use /s for show, /f for setting the font, /c for curves, and so on.
•For procedures that are called frequently in the script of a document but which are not the “workhorses,” use two-letter names rather than using one-letter names that are not mnemonic. For example, it is better to use /ml for { moveto lineto } than to use, say, /q.
•For procedures that are called from within other procedures, use descriptive names like /newline or /reencodefont.
Chapter 9: PROCEDURES |
113 |