In postscript, the roll
operator is very general and difficult to visualize. How do you make sure you're rolling in the right direction?
I want to get a solid handle on roll
because I want to be able to transform functions using variables
/f { % x y z
/z exch def
/y exch def
/x exch def
x dup mul
y dup mul
z dup mul add add % x^2+y^2+z^2
} def
into functions using stack manipulations, more like
/f { % x y z
3 1 roll dup mul % y z x^2
3 1 roll dup mul % z x^2 y^2
3 1 roll dup mul % x^2 y^2 z^2
add add % x^2+y^2+z^2
} def
or
/f { % x y z
3 { 3 1 roll dup mul } repeat
2 { add } repeat % x^2+y^2+z^2
} bind def
These should both execute faster by having fewer name-lookups (hashtable search).
With roll
I always have to test it out; and I usually
get it wrong on the first try! I'm okay with exch, though
I had difficulty with roll for a very long time. I remember it now using these ways, which are all equivalent:
n j roll
positive j, to roll away
7 8 9 3 1 roll
% 9 7 8
negative, to get it back (or "negateeve, to then retrieve")
% 9 7 8
3 -1 roll
% 7 8 9
Perhaps a better way to think of it is a physical stack (of books, say) so the top of stack is literally "on top".
Then a positive roll goes up:
for j number of times pick up n books put the top one on the bottom (shifting the substack "up") put them back down
And a negative roll goes down:
for j number of times pick up n books put the bottom one on top (shifting the substack "down") put them back down
But I usually picture the stack sideways, the way the objects would look in a file as a sequence of literals. So I think of the positive roll as stashing the top j things behind the nth thing; and the negative roll as snagging j things starting with the nth thing. Give and Take.
Away.
n j roll
__ j > 0 __ move top j elements to the bottom of n
n TOS
-------------|
| j |
| -----|
| | |
V V |
a b c d e f g h
^ | |
| |-------|
^ |
-<-<-<-<-<-
move
And back.
__ j < 0 __ move j elements from the bottom of n to the top
n TOS
-------------|
| j |
|----- |
| | |
V V |
a b c d e f g h
| | ^
|-------| |
| ^
->->->->->-
move
Still another way is to picture it sideways, and laying a sticky wheel on top (a lint-roller, maybe)
(a) (b) (c) (d) (e) 5 3 roll _______ / \ | 3 | | / | \ | \_______/ (a) (b) (c) (d) (e)
Then a positive roll goes counterclockwise just like arc and rotate.
_______ (e) / / \ | 3 --| (d) | \ | \_______/ (c) (a) (b) (e)__(d)__(c) /\ | /\ | 3 | | | \_______/ (a) (b) (c)_______ /\ \ (d) |-- 3 | |/ | \_______/ (e) (a) (b) _______ / \ | 3 | | / | \ | \_______/ (c) (d) (e) (a) (b)
And a negative roll goes clockwise like arcn and a negative rotation.
_______ / \ | 3 | | / | \ | \_______/ (a) (b) (c) (d) (e) (a)_______ /\ \ (b) |-- 3 | |/ | \_______/ (c) (d) (e) (c)__(b)__(a) /\ | /\ | 3 | | | \_______/ (d) (e) _______ (c) / / \ | 3 --| (b) | \ | \_______/ (a) (d) (e) _______ / \ | 3 | | / | \ | \_______/ (d) (e) (a) (b) (c)
It shouldn't be difficult to see that negative rolls are entirely unnecessary because if j<0, it can be replaced by n-j. eg.
3 -1 roll % roll bottom 1 element from 3 to the top
3 2 roll % roll top 2 elements behind the 3rd
are the same.
16 -4 roll % roll bottom 4 elements from 16 to the top
16 12 roll % roll top 12 elements behind the 16th
are the same.
This leads to the final, ultimate simplified view (though each of the above will work, too).
You're really just exchanging the top j elements with the n-j elements below that.
Say you have this mess on the stack (where $TOS$ marks the top of the stack), and want to order it properly:
g h i j k l m n o p q r s t u v w x y z a b c d e f $TOS$
Count up (down) for n and j.
g h i j k l m n o p q r s t u v w x y z a b c d e f
26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
| | j = 6 . . . .
| n = 26 . . . . . . . . . . . . . . . . . . . . . . .
> 26 6 roll pstack
a b c d e f g h i j k l m n o p q r s t u v w x y z
A negative value for j simply positions that dividing line relative to the deepest element from among the n elements (it counts from below).
t u v w x y z a b c d e f g h i j k l m n o p q r s
26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
. . . . j = -7 | |
. . . . . . . . . . . . . . . . . . . . . . . n = 26 |
> 26 -7 roll pstack
a b c d e f g h i j k l m n o p q r s t u v w x y z
Here is a convenience function that gives an interface to roll that's more closely analogous to the big swap view.
% r0..rN s0..sM N M swap s0..sM r0..rN
% a gentler interface to the power of roll
/swap {
exch 1 index add exch
roll
} def
0 1 2 3 /a /b /c 4 3 swap pstack
Output:
GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
3
2
1
0
/c
/b
/a
GS<7>GS<7>