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

Figure 6.1: Anatomy of the roll Operator

miscellaneous data already on the stack

/A /B /C /D /E /F /G 4 -1 roll

roll amount

roll group

One helpful way to conceptualize the roll operator is to verbalize it. For the example shown in Figure 6.1, one might say “Take the top four elements on the stack and roll it negative one times.” The top four elements are easy enough to understand, but what does it mean to roll something “negative one times?” The sign of the number (negative or positive) indicates the direction to roll, and the magnitude of the number indicates the number of elements to be rolled. The easiest way to think of the direction of the roll is like this:

If the roll amount is negative, that means you take the elements from the bottom of the roll group and bring them to the top.

If the roll amount is positive, you take the elements from the top of the stack and roll them to the bottom of the roll group.

Figure 6.2 and Figure 6.3 show the effects of rolling with a positive and a negative roll amount.

CONDITIONALS AND LOOPS

One of the most difficult things to do in PostScript programming is to make sure you don’t inadvertently leave something on the stack or consume something from the stack that you weren’t supposed to. Keeping track is easy enough when there is only a single path through the code, but if there are lots of ifelse statements and loop constructs, it gets more difficult to trace through all the possibilities to make sure you’ve not forgotten anything.

One of the most common instances of this problem is caused by operators that return different numbers of arguments depending on whether they succeed or fail. Example 6.3 shows a conditional that accidentally leaves

Chapter 6: TRUSTING THE STACK

73

a dictionary behind on the operand stack when the where operator succeeds (returns true), but works fine when the operator returns false.

Figure 6.2: Positive Rolling

roll group of 6

/A /B /C /D /E /F /G 6 2 roll

roll amount 2

roll direction “to bottom of roll group”

/A /B /C /D /E /F /G

6 2 roll

/A /F /G /B /C /D /E

result of 6 2 roll

Figure 6.3: Negative Rolling

roll group of 6

/A /B /C /D /E /F /G 6 -2 roll

roll amount -2

roll direction “to top of roll group”

/A /B /C /D /E /F /G

6 -2 roll

/A /D /E /F /G /B /C result of 6 -2 roll

74

Chapter 6: TRUSTING THE STACK

Example 6.3: Accidentally Leaving Dictionary on Stack

/selectfont where not { %if /selectfont { %def

exch findfont exch scalefont setfont

} def

} if

Unfortunately, the where operator returns the dictionary in which the key is found in the event that it does find the key, but returns nothing other than the boolean if it does not find the key. Example 6.3 shows a simple fix to correct this problem. The if statement is changed to ifelse, and the extra dictionary is popped from the stack if the where operator returns true.

Example 6.4: Correctly Maintaining Dictionary Stack

/selectfont where not { %if /selectfont { %def

exch findfont exch scalefont setfont

} def

 

}{ %else

 

pop

% throw away dictionary returned by “where”

} ifelse

 

Table 6.2 provides a list of operators that can cause you some trouble by returning sometimes unexpected results on the operand stack. If you get a typecheck error that you have trouble finding, this might be the cause.

Table 6.2: Operators with Unexpected Stack Results

Operator

Results Returned

anchorsearch

different results on success and failure

for

unexpected loop index

forall

different results with different data types

scalefont

returns scaled dictionary onto stack

search

different results on success and failure

stringwidth

unexpected extra y value

where

different results on success and failure

Chapter 6: TRUSTING THE STACK

75

RECURSION AND LOCAL VARIABLES

If you create a procedure that calls itself recursively, you have to be careful about the names you use. Recursion is inherently stack-based. To make recursion work correctly in PostScript, you need either to use the dictionary stack or the operand stack to store intermediate results until your recursion is unwound back to the original invocation level.

Let’s look at a recursive function that has an integer passed to it as an argument, and that will ultimately return an integer as its result (Example 6.5). If the argument is even, the function calls itself recursively and adds one to the argument. If the argument is odd, the function returns. The result of this function is always an odd number.

Example 6.5: Recursion Using the Dictionary Stack

/recurse_proc

% int recurse_proc int

{ %def

 

save

2 dict begin

/save_obj exch def /arg exch def

arg 2 div truncate 2 mul cvi

arg eq { %ifelse

% even number

arg 1 add recurse_proc }{ %else

arg

} ifelse

 

save_obj

% leave on stack

end

 

restore

% to save_obj on stack

} bind def

 

2 recurse_proc

 

If you want to store the function’s argument in a dictionary, you have to create a new dictionary and push it onto the dictionary stack each time the function is called, to maintain the name local to that instance of the function. In this example, the memory allocated by the dictionary is reclaimed by save and restore, putting each save object into the recursion dictionary until it is needed. If the function is called recursively, more

76

Chapter 6: TRUSTING THE STACK