<< . .

. 22
( : 45)

. . >>

De¬ne a ˜transum™ macro operation that yields the sum of two transforms.
(If t3 = t1 transum t2 , then z transformed t3 = z transformed t1 + z transformed t2 ,
for all pairs z.)
Chapter 20: More About Macros 179

Now we™ve covered all the types of de¬nition , and it™s time to take stock and mastication
expansion process
think about the total picture. ™s mastication process converts an
digestion process
input ¬le into a long sequence of tokens, as explained in Chapter 6, and its digestive Conditions
processes work strictly on those tokens. When a symbolic token is about to be digested, if
looks up the token™s current meaning, and in certain cases will elseif
expand that token into a sequence of other tokens before continuing; this “expansion else
process” applies to macros and to if and for, as well as to certain other special prim-
itives that we shall consider momentarily. Expansion continues until an unexpandable forsu¬xes
token is found; then the digestion process can continue. Sometimes, however, the ex- forever
pansion is not carried out; for example, after has digested a def token, it
stops all expansion until just after it reaches the corresponding enddef . A complete input
list of all occasions when tokens are not expanded appears later in this chapter. ¬lename
Let™s consider all the tokens that cause expansion to occur, whenever expan- backslash
sion hasn™t been inhibited:

Macros. When a macro is expanded, ¬rst reads and evaluates the
arguments (if any), as already explained. (Expansion continues while expr and su¬x
arguments are being evaluated, but it is suppressed within text arguments.) Then
replaces the macro and its arguments by the replacement text.
Conditions. When ˜if ™ is expanded, reads and evaluates the
boolean expression, then skips ahead, if necessary, until coming to either ˜¬™ or a
condition that™s true; then it will continue to read the next token. When ˜elseif ™ or
˜else™ or ˜¬™ is expanded, a conditional text has just ended, so skips to the
closing ˜¬™ and the expansion is empty.
Loops. When ˜for™ or ˜forsu¬xes™ or ˜forever™ is expanded,
reads the speci¬cations up to the colon, then reads the loop text (without expansion)
up to the endfor. Finally it rereads the loop text repeatedly, with expansion. When
˜exitif ™ is expanded, evaluates the following boolean expression and throws
away the semicolon; if the expression proves to be true, the current loop is terminated.
scantokens string primary . When ˜scantokens™ is expanded,
evaluates the following primary expression, which should be of type string. This string
is converted to tokens by the rules of Chapter 6, as if it had been input from a ¬le
containing just one line of text.
input ¬lename . When ˜input™ is expanded, the expansion is null, but
prepares to read from the speci¬ed ¬le before looking at any more to-
kens from its current source. A ¬lename is subject to special restrictions explained
on the next page.
endinput. When ˜endinput™ is expanded, the expansion is null. But the
next time gets to the end of an input line, it will stop reading from the
¬le containing that line.
expandafter. When ˜expandafter™ is expanded, ¬rst reads one
more token, without expanding it; let™s call this token t. Then reads the
token that comes after t (and possibly more tokens, if that token takes an argument),
replacing it by its expansion. Finally, puts t back in front of that expansion.
\. When ˜\™ is expanded, the expansion is null, i.e., empty.
180 Chapter 20: More About Macros

The syntax for ¬lename is not standard in , because di¬erent ¬le names
operating systems have di¬erent conventions. You should ask your local sys-
tem wizards for details on just how they have decided to implement ¬le names. The scantokens
situation is complicated by the fact that ™s process of converting to tokens delimiters
is irreversible; for example, ˜x01™ and ˜x1.0™ both yield identical sequences of tokens. let
Therefore doesn™t even try to convert a ¬le name to tokens; an input op- newinternal
eration must appear only in a text ¬le, not in a list of tokens like the replacement text
of a macro! (You can get around this restriction by saying showvariable
scantokens "input foo" everyjob

or, more generally,
scantokens ("input " & fname )
if fname is a string variable containing the ¬lename you want to input.) Although
¬le names have nonstandard syntax, a sequence of six or fewer ordinary letters and/or
digits followed by a space should be a ¬le name that works in essentially the same way
on all installations of . Uppercase letters are considered to be distinct from
their lowercase counterparts, on many systems.
Here now is the promised list of all cases when expandable tokens are not ex-
panded. Some of the situations involve primitives that haven™t been discussed
yet, but we™ll get to them eventually. Expansion is suppressed at the following times:
When tokens are being deleted during error recovery (see Chapter 5).
When tokens are being skipped because conditional text is being ignored.
When is reading the de¬nition of a macro.
When is reading a loop text, or the symbolic token that immedi-
ately follows for or forsu¬xes.
When is reading the text argument of a macro.
When is reading the initial symbolic token of a declared variable
in a type declaration.
When is reading the symbolic tokens to be de¬ned by delimiters,
inner, let, newinternal, or outer.
When is reading the symbolic tokens to be shown by showtoken
or showvariable.
When is reading the token after expandafter, everyjob, or the
˜=™ following let.
The expansion process is not suppressed while reading the su¬x that follows the initial
token of a declared variable , not even in a vardef heading .
Chapter 20: More About Macros 181


The two lieutenants,
Fonteius Capito in Germany,
and Claudius Macro in Africa,
who opposed his advancement,
were put down.
” SUETONIUS, Sergius Sulpicius Galba (c. 125 A.D.)

By introducing macro instructions in the source language,
the designer can bring about the same ease of programming
as could be achieved by giving the computer
a more powerful operation list than it really has.
But naturally, one does not get the same advantages
in terms of economy of memory space and computer time
as would be obtained if the more powerful instructions
were really built into the machine.
” O. DOPPING, Computers & Data Processing (1970)
(page 182)

Chapter 21: Random Numbers 183

It™s fun to play games with by writing programs that incorporate chance
an element of chance. You can generate unpredictable shapes, and you can uniformdeviate
add patternless perturbations to break up the rigid symmetry that is usually normaldeviate
random number
associated with mathematical constructions. Musicians who use computers to bell-shaped
synthesize their compositions have found that music has more “life” if its rhythms scatter plots
are slightly irregular and o¬beat; perfect 1“2“3“4 pulses sound pretty dull by
contrast. The same phenomenon might prove to be true in typography.
allows you to introduce controlled indeterminacy in two
ways: (1) ˜uniformdeviate t™ gives a number u that™s randomly distributed be-
tween 0 and t; (2) ˜normaldeviate™ gives a random number x that has the
so-called normal distribution with mean zero and variance one.
More precisely, if t > 0 and u = uniformdeviate t, we will have 0 ¤ u < t,
and for each fraction 0 ¤ p ¤ 1 we will have 0 ¤ u < pt with approximate
probability p. If t < 0, the results are similar but negated, with 0 ≥ u > t. Finally if
t = 0, we always have u = 0; this is the only case where u = t is possible.
A normaldeviate, x, will be positive about half the time and negative about
half the time. Its distribution is “bell-shaped” in the sense that a particular
value x occurs with probability roughly proportional to e’x /2 ; the graph of this func-
tion looks something like a bell. The probability is about 68% that |x| < 1, about 95%
that |x| < 2, and about 99.7% that |x| < 3. It™s a pretty safe bet that |x| < 4.

Instead of relying on mathematical formulas to explain this random be-
havior, we can actually see the results graphically by letting draw
some “scatter plots.” Consider the following program, which draws a 10 pt—10 pt
square and puts 100 little dots inside it:
beginchar (incr code , 10pt #, 10pt #, 0);
pickup pencircle scaled .3pt ; draw unitsquare scaled w;
pickup pencircle scaled 1pt ;
for k = 1 upto 100:
drawdot(uniformdeviate w, uniformdeviate w); endfor endchar.
The resulting “characters,” if we repeat the experiment ten times, look like this:
And if we replace ˜uniformdeviate w™ by ˜.5w + w/6 — normaldeviate™, we get
Finally, if we say ˜drawdot(uniformdeviate w, .5w + w/6 — normaldeviate)™ the
results are a mixture of the other two cases:
Consider the program fragment ˜if uniformdeviate 1 < 1/3: case a else: case b ¬™.
True or false: case b will occur about three times as often as case a .
184 Chapter 21: Random Numbers

EXERCISE 21.2 logo
™s uniformdeviate operator usually doesn™t give you an integer. Ex-
plain how to generate random integers between 1 and n, in such a way that each
value will be about equally likely.
What does the formula ˜(uniformdeviate 1)[z1 , z2 ]™ represent?
Guess what the following program will produce:
beginchar(incr code,100pt#,10pt#,0);
for n:=0 upto 99:
fill unitsquare xscaled 1pt yscaled uniformdeviate h
shifted (n*pt,0); endfor endchar.
And what does this puzzle program draw?
beginchar(incr code,24pt#,10pt#,0);
numeric count[];
pickup pencircle scaled 1pt;
for n:=1 upto 100:
if unknown count[y]: count[y]:=-1; fi
drawdot(x,pt*incr count[y]); endfor endchar.

Let™s try now to put more “life” in the logo, by asking Lady Luck
to add small perturbations to each of the key points. First we de¬ne noise ,

vardef noise = normaldeviate — craziness enddef ;

the craziness parameter will control the degree of haphazard variation. Then we can
write the following program for the logo™s ˜ ™:

beginlogochar ("N", 15);
x1 = leftstemloc + noise ;
x2 = leftstemloc + noise ;
w ’ x4 = leftstemloc + noise ;
w ’ x5 = leftstemloc + noise ; (Figure 21a will be inserted here; too
bad you can™t see it now.)
bot y1 = noise ’ o ;
top y2 = h + o + noise ;
y3 = y4 + ygap + noise ;
bot y4 = noise ’ o ;
top y5 = h + o + noise ;
z3 = whatever [z4 , z5 ];
draw z1 - - z2 - - z3 ; draw z4 - - z5 ; labels(1, 2, 3, 4, 5); endchar.

The illustration here was drawn with craziness = 0, so there was no noise.
Chapter 21: Random Numbers 185

Three trials of the 9 pt ˜ ™ with craziness = .1pt gave the following results: randomseed

(Figure 21b&c&d will be inserted here; too bad you can™t see it now.)

And here™s what happens if you do similar things to all the letters of , with
craziness decreasing from .45pt to zero in steps of .05pt :

Every time you run a program that refers to random numbers, you™ll get
di¬erent results, because uses the date and time of day to change
its generator. This unpredictable behavior is normally what you want, but it can be
troublesome if your program draws a lovely shape that you™d like to see again. Or
perhaps one of your runs will uncover a program bug; you won™t be able to diagnose
the problem, because it probably won™t recur! The solution is to say
randomseed := numeric expression
and to remember the value of that numeric expression. (The value will automatically
be recorded in the transcript ¬le of your run.) You will get the same sequence of
uniform and normal deviates on any two runs that begin with the same randomseed,
because ™s numbers are only “pseudo-random.”

A musician whom I knew amused himself
by tuning his piano arbitrarily, without any rhyme or reason.
Afterwards he played Beethoven™s Sonate Path´tique by heart.
It was an unbelievable delight to hear an old piece come back to life.
I had heard this sonata for twenty years,
never dreaming that it was capable of being developed further.
” AUGUST STRINDBERG, Chance in Artistic Creation (1894)

[Education] must lead us from chance and arbitrariness
to rational clarity and intellectual order.
” L. MIES VAN DER ROHE, Inaugural Address (1938)
(page 186)

Chapter 22: Strings 187

is not a word processor, but a programmer can process ditto
words and other short strings of symbols in rudimentary ways. Strings can tracingtitles
help explain what a program is doing; for example, the io.mf ¬le of Chapter 5 proo¬ng
mentions "The letter O" as a title that should appear on proofsheets, and it labels
also says "O" in order to identify the position of a character in the output font. concatenation
Chapter 6 points out that a string token is any sequence of characters string primary
enclosed in double-quote (") marks, except that you™re not allowed to use the (
double-quote character itself in this way. If you need that character, plain - begingroup
provides it in a string of length 1 called ditto . Thus endgroup
"A string expression can contain a ˜" & ditto & "™ mark" str
even though a string token cannot. decimal
A string expression can be used all by itself as a statement, just as if it
were an equation or declaration or command. Such a statement is called a title , string secondary
string tertiary
provided that it is immediately followed by a ˜;™. If tracingtitles > 0 when a
string expression
title is encountered, will type the title on the user™s terminal. If &
proo¬ng > 0 when a title is encountered, will copy the title into the
command line
output ¬le, so that it can be put onto proofsheets by postprocessors such as the mfput
GFtoDVI program described in Appendix H.
Appendix H explains how to specify the strings that are used as labels for the
key points on proofsheets.
Here™s the full syntax for string expressions. All of the activity except for
concatenation (˜&™) takes place at the primary level:
string primary ’’ string token
| string variable
| ( string expression )
| begingroup statement list string expression endgroup
| jobname
| readstring
| str su¬x
| char numeric primary
| decimal numeric primary
| substring pair primary of string primary
string secondary ’’ string primary
string tertiary ’’ string secondary
string expression ’’ string tertiary
| string expression & string tertiary
The new features here are jobname, readstring, str, char, decimal, and substring;
we shall consider each of them in turn.
The name of your job ( jobname) is the name of the ¬rst ¬le you input,
provided that the ¬rst line of instructions to (the ˜**™ line or
command line) causes input of some ¬le. Otherwise the job name is mfput, as in
Experiment 1 of Chapter 5.
188 Chapter 22: Strings

When you say ˜readstring™, stops and waits for the user to type readstring
a line at the terminal. The value of readstring is the contents of this line,
with trailing spaces eliminated. (You probably should use the message command str
¬rst, to give the user a clue about what to type; for example, see the expr.mf ¬le char
of Chapter 8, which gets its input expressions via readstring. The stop macro of decimal representation
Appendix B makes use of the fact that readstring halts the computer; it doesn™t substring
actually look at the string.)
graph paper
An arbitrary su¬x is converted to a string by str, using the method by ASCII
which displays su¬x arguments in diagnostic typeouts. Negative oct
octal notation
subscripts are enclosed in square brackets; spaces or dots are inserted between tokens
whose characters belong to the same class (according to the table in Chapter 6). For hexadecimal notation
example, if n = 1 then ˜str x[n]a™ is "x1a"; ˜str x n a™ is "x.n.a".

The result of ˜char n™ is a string of length 1, representing the character whose
ASCII code is n. (Appendix C explains this code.) The value of n is
¬rst rounded to the nearest integer, then multiples of 256 are added or subtracted if
necessary until 0 ¤ n < 256; this de¬nes char n in all cases.

The decimal representation of a known numeric value x is available in string
form as ˜decimal x™. If x is negative, the ¬rst character of this string will
be ˜-™. If x is not an integer, a decimal point will be included, followed by as many
digits as are necessary to characterize the value. (These conventions are the same as
those illustrated in the example outputs of Chapter 8.)

The rules for substring are like the rules for subpath in Chapter 14. -
thinks of a string as if its characters were written in the squares of a
piece of graph paper, between coordinates x = 0 and x = n, where n is the length
of the string. In simple cases, substring (a, b) then refers to the characters between
x = a and x = b. The rules for the general case are slightly more involved: If b < a, the
result will be the reverse of substring (b, a). Otherwise a and b are replaced respectively
by max(0, min(n, round a)) and max(0, min(n, round b)); this leads to the simple case
0 ¤ a ¤ b ¤ n described above, when the resulting string has length b ’ a.

Strings can be converted into numbers, although Chapter 8 didn™t mention
this fact in its syntax for numeric primary . The primitive operations are

ASCII string primary | oct string primary | hex string primary

where ˜ASCII™ returns the ASCII code of the ¬rst character of the string, ˜oct™ computes
an integer from a string representing octal notation (radix 8), and ˜hex™ computes an
integer from a string representing hexadecimal notation (radix 16). For example,

ASCII "100" = 49; oct "100" = 64; hex "100" = 256.

Several exceptional conditions need to be mentioned: (1) ASCII "" = ’1; otherwise
ASCII yields an integer between 0 and 255. (2) The characters in the string argument
to ˜oct™ must all be digits in the range 0“7. (3) The characters in the string argument
to ˜hex™ must all be digits in the range 0“9, A“F, or a“f. (4) The number that results
from ˜oct™ or ˜hex™ must be less than 4096. Thus, ˜oct "7777"™ and ˜hex "FFF"™ are the
maximum legal values.
Chapter 22: Strings 189

<< . .

. 22
( : 45)

. . >>