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

Example 7.3: Thinking through a Conditional

% if the variable is greater than three, myvariable 3 gt { %ifelse

% then set it back to 0, /myvariable 0 def

}{ %else

%otherwise, add one to it and tell me its current value: /myvariable myvariable 1 add store

(my variable is now ) print myvariable == flush

} ifelse

As long as you think about your algorithm as a straightforward sequence of steps and can figure out how to write the expression and each of the clauses, the ifelse statement itself should be easy to master. You can lay it out in a template in seconds. Then you are left with the task of the carefully implementing the algorithm, not the details of the conditional statement.

The major advantage to the indentation style and position of the { } braces seen in these examples is that program lines can be easily added to or deleted from the clauses of the ifelse statement without disrupting the syntactic balance of the braces. This becomes especially important when braces are nested several deep.

SETTING UP THE CONDITION

All conditional statements depend on something being true or false. There are many PostScript operators that return a boolean value (remember, this just means that it can only be true or false). The purpose of this boolean returned on the operand stack is so that you can use it with an ifelse statement. The simplest of these is the eq operator (see Example 7.4).

Example 7.4: Setting Up the Conditional

dup type /stringtype eq { %ifelse

%topmost element on stack is a string

}{ %else

%it is not a string

}ifelse

Chapter 7: BUILDING CONDITIONAL STATEMENTS

81

Remember that comparing an object to another object with eq pops both objects from the stack before pushing the result of the comparison. If you need the object again, you should either use dup to make a copy before you compare, or give the object a name in a dictionary (see Declaring and Using Variables in Chapter 3).

There are many other operators that return booleans. For example, the readhexstring operator returns a boolean, even though most of the examples of its use (with the image operator, especially) simply throw the boolean away. For a more careful use of this boolean, see Example 7.5.

Example 7.5: Using a Conditional with readhexstring

currentfile 128 string readhexstring { %ifelse

%readhexstring filled up the buffer

}{ %else

%it encountered EOF or other problems

}ifelse

Table 7.1 shows the most useful PostScript operators that return booleans. An ifelse or if statement can follow any of these operators. In fact, the operators were designed to be followed by a conditional.

Table 7.1: PostScript Operators that Return Boolean Values

Arguments Operator

Action

any1 any2

eq bool

test equal to

any1 any2

ne bool

test not equal to

obj1 obj2

ge bool

test greater than or equal to

obj1 obj2

gt bool

test greater than

obj1 obj2

le bool

test less than or equal to

obj1 obj2

lt bool

test less than

bool1 bool2

and bool3

logical and

bool1

not bool2

logical not

bool1 bool2

or bool3

logical inclusive or

bool1 bool2

xor bool3

logical exclusive or

±

true true

push boolean value true

±

false false

push boolean value false

dict key

known bool

test whether key is in dict

key

where dict true

find dict in which key is defined

 

false

 

82

Chapter 7: BUILDING CONDITIONAL STATEMENTS

string seek

anchorsearch post

determine if seek is initial substring

 

 

 

match

of string

 

 

 

true

 

 

or

 

string

or return string if not

 

 

 

false

 

string seek

search post match pre

search for seek in string

 

 

true

 

 

or

string false

 

string

token post token true

read a token from start of string

 

 

false

 

any stopped bool

execute and catch any call to stop

any xcheck bool

test executable attribute

string

rcheck bool

test read access

string

wcheck bool

test write access

file_object

read int true

read one character from file

 

 

false

 

file_object string

readhexstring

read string in hexadecimal from

 

 

substring

file_object into buffer string

 

 

boolean

 

file_object string

readline

string bool

read through newline

file_object

token token true

read PostScript token from file

 

 

false

 

file_object

status

boolean

is file_object still valid?

NOTE: Operators marked by a dagger (†) are not available in all interpreters; check before executing.

It is also useful to combine a few simple operations into powerful tests. In Example 7.6 are some techniques for type checking and other tasks, in the context of other simple procedures.

The program in Example 7.7 will open up a file from the disk and print (or display) it one line at a time, starting a new page as necessary. There are several interesting conditionals here, based on the current point and the results of the readline operator.

Chapter 7: BUILDING CONDITIONAL STATEMENTS

83

Example 7.6: Procedure with Typechecking

/typecheck_moveto { %def

% if either argument is not a number, use 0 dup type /realtype ne

1 index type /integertype ne or { %if pop 0

}if exch

dup type /realtype ne

1 index type /integertype ne or { %if pop 0

}if

exch moveto } bind def

Example 7.7: Conditionals at Work

%note: this program will only work on a UNIX workstation, since it explicitly

%opens the file /etc/passwd and uses the non-standard “selectfont” operator /bottom 72 def

/lineshow

% (string) lineshow -

{ %def

 

%works like show, but checks for bottom

%of page and also moves current point

%down one line after each call currentpoint exch pop% just the Y coord bottom lt { %if

showpage

72 750 moveto% top of new page

} if

gsave show grestore

0 -12 rmoveto

% down one line

} bind def

 

%list the /etc/passwd file /Times-Roman 10 selectfont 72 750 moveto

/datafile (/etc/passwd) (r) file def /buffer 256 string def

84

Chapter 7: BUILDING CONDITIONAL STATEMENTS

{ %loop

datafile buffer readline { %ifelse lineshow

}{ %else

fd closefile showpage exit

} ifelse

 

} bind loop

 

 

A very common source of program bugs is to have one else clause in a

 

conditional that leaves something on the operand stack (or uses too many

 

elements from it). If you change the conditional expression so that the

 

stack holds different elements, be careful to debug each of the individual

 

clauses fully. If the else clauses handle obscure possibilities or error

 

conditions, they may not be executed (and hence debugged) under normal

 

testing, but may fail under heavy use. Watch for this source of errors and

 

bugs, and try to develop a sound methodology that will help to eliminate

 

the cause of them.

 

 

 

 

 

TIP

It is important to test both the true and false clauses of your conditional

 

statements, especially as you gradually change your code and evolve the

 

contents or the type of items on the stack before going into the condition-

 

al. You can “rig” this testing by redefining the ifelse operator to reverse

 

the sense of all your conditionals (see Example 7.8), just to give a dry run

 

through them, or you can test each one by hand.

 

 

 

 

 

Example 7.8: Redefining ifelse for Testing

 

% flip the sense of “ifelse” so you can test both sides of the operation

 

/orig_ifelse /ifelse load def

 

/ifelse

% boolean { true_proc } { false_proc } ifelse

 

{

 

 

exch

% execute procs in other order

 

orig_ifelse

 

 

} bind def

 

As you gain more experience with the language, you will become expert in the use of the ifelse statement, which is a fundamental and useful operator in any PostScript language program.

Chapter 7: BUILDING CONDITIONAL STATEMENTS

85