Why did I come so far ?
Guess I must have forgotten
that I'd have to walk back again too !
Hilah Paulmier and Robert Haven Schauffler - Peace Days
STRENGTH IN FORTH
part thirteen
SOME LESSONS IN LATIN AND OTHER STUFF
There happens to be something evil in the air regarding this part
of the FORTH-course and it may bring me a lot of misfortune. I
can smell it !! Part thirteen !! And I already saw a black cat
this morning too ! Long ago (?) superstition was very common
among us, people. Animals are far too smart to be superstitious.
Yes, a dog will get mad at the sight of a black cat and at a red
one too, to prove that it's its nature. Perhaps it is mankind's
nature to be superstitious. Who knows ? It seems they love to get
frightened in some irrational way mostly by pretty harmless
animals or events.
RUNNING AROUND
In the same way, some people are afraid of words: especially of
so called difficult words, even if you tell one, that there are
no difficult words. People themselves are often more difficult
than the words they speek. If we speek about computers and about
all that can be done with them, we often encounter 'difficult'
notions. Let me name one: recursion. It's Latin and I can assure
you that it was not a difficult word to those Romans conquering
the world as they thought they did 20 centuries ago. Recursion
consist of two parts: re and cursion, in Latin: re and curere.
The first word isn't really a word. It is a prefix. It is often
put in front of a verb or a noun to indicate, that some event
happens again and again. The word curere means to run. So
recursion may be translated by to rerun. In computerenvironments
it has something to do with a program or a part of a program,
that has in effect to be rerun in one way or another. Yes, and
curere has much to do with cursor (a runner) and a course: a
learning-program that you 'run' through. And with a carreer. If
you were an Arab, you wouldn't have written the vowels of a word.
It's common use in Arabian languages to left out those sounds.
Now if you act with the 'curere'-words as an Arab, you will find
out that crr and crs are the favourite letter-combinations of
this Latin word. They appear in curere and carreer, in cursor and
course. And in French courir (crr) means to run. You see, it all
sticks together. So, recursion is related to 'repeat' ? In a way,
yes ! In another way, no ! If recursion would have been identical
to repetition, then the repeated action inside a loop could have
been called recursion too ! The one restriction with a loop is,
that a loop does NOT repeat ITSELF in itself as a loop. The
other is, that you can't start the loop again from within the
loop. And the BEGIN..WHILE...REPEAT-loop, what about it, you
might have argued, if you had carefully thought over my above
statements (Which you did NOT, did you ?). That objection is not
a valuable one. Because you can't jump nor from within
BEGIN..WHILE nor from within WHILE..REPEAT to BEGIN. If what has
been said above, is all clear to you, we can add the following
statement: a recursive action is an action which uses itself as a
part of its own definition. In FORTH a recursive definition is
often and easily recognised by the word {MYSELF}. So
: RERUN (words) MYSELF (more words) ;
is a recursive definition. The execution of {RERUN} will at a
certain moment execute {MYSELF}, which causes a call back to
{RERUN}. The word {MYSELF} is a most popular solution in FORTH to
the problem of how to construct recursive definitions. {MYSELF}
will find the address of the namefield of the word currently
being defined first, then it will calculate the compilation field
address and compile this address into into the dictionary. It's
an immediate word, because it has to execute during compilation
of the word in which it is used. The following example is a quite
complicated one regarding the fact, that this course happens to
be for beginners only. Don't type it at your terminal. Executing
this example may cause a returnstack overflow or your FORTH may
start doing crazy things.
: ?THOUSAND ( n --- n.....1000 )
DUP 1000 <
IF 1+
CR DUP .
MYSELF
THEN
;
A ROUTINE OF A SORT
Nevertheless this definition is recursive all the way. What will
this definition do, if we execute it in our minds. As you can see
by the stacknotation, a number is expected. Try this one
1230 {?THOUSAND} OK
Your computer wouldn't have collapsed if you had entered that
line. The {IF..THEN}-structure would have jumped directly beyond
{THEN}, as the result of the comparison 1230 1000 {<} is false.
So the sequence inside the {IF..THEN} isn't executed. I simply
avoided the confrontation with {MYSELF} ! Try next one
950 {?THOUSAND} 950 951 etc....1000 OK
The output at your screen is vertically instead, but still there
will not be a hang-up of your machine. I tested 4 different
FORTH-systems: two of them - after 57 and 67 calls from {MYSELF}
to {?THOUSAND} respectively - displayed OK and that was it. One
system could stand 510 calls and then warned me: Returnstack
overflow. The last one also held it for 510 calls and then
collapsed without any message or proper warning. So recursion has
its limits, set by the size of the returnstack. Last try......
400 {?THOUSAND} 401 .........??????????????
From the above follows, that this sequence will not do, what it
was intended to do when I defined the definition of {?THOUSAND}.
That definition says: if a number is smaller than 1000, add 1 to
that number, take a new line, duplicate the increased number,
take the duplicate one from the stack and print it at the screen
and then branch to {?THOUSAND} again to test if the number is
still smaller than 1000, if so add 1 again etcetera. If the
number is not smaller than 1000 any longer, then the word
{?THOUSAND} will end. Of course the phenomenon of a returnstack
being too small to hold a thousand numbers is worth asking
questions about it. This returnstack has never caused us any
trouble so far. But before I'll try to answer that question, I'll
try to comfort you. I'll give you a recursive word which is a
quicksort-routine. In part 8 of this course I gave you a bubble-
sorting routine. Some of the code involved in that routine we are
going to use here too.
: LARRAY CREATE 4* ALLOT
DOES> SWAP 4* +
;
256 LARRAY S-D
511 CONSTANT RAND
: FILL-ARRAY 256 0 DO
RAND RND I S-D !
LOOP
;
: SHOW-ARRAY 252 12 / 0 DO
12 0 DO
J 12 * I + S-D @
6 .R
LOOP CR
LOOP
;
: I' RP@ 4 + @ ;
: I'' RP@ 8 + @ ;
: QSORT 2DUP >R >R I' I'' + 2/ S-D @
BEGIN
BEGIN I' S-D @ OVER < WHILE R> 1+ >R REPEAT
BEGIN DUP I'' S-D @ < WHILE R> R> 1- >R >R REPEAT
I' I'' > NOT IF
I' S-D @ I'' S-D @
I' S-D ! I'' S-D !
R> 1+ R> 1- >R >R
THEN
I' I'' > UNTIL
DROP R> SWAP ROT R>
2DUP < IF MYSELF THEN
2OVER < IF 2SWAP MYSELF THEN
2DROP
;
: QUICKSORT QSORT 2DROP ;
FROM HIGH TO LOW AND BACK
First I'll better tell you how to use it, then I'll give an
explanation of how it works and last of all I'll show you how to
adapt it - if necessary - to your system. The {QUICKSORT}-word in
this form will sort from low to high 256 randomely chosen numbers
in less than a second. The numbers are put into an array {S-D} by
the word {FILL-ARRAY}. If you want to have a look at the newly
filled array, then type {SHOW-ARRAY} and on a b/w-monitor 252
numbers will be displayed neatly grouped into columns. Now you
can sort the numbers by typing 0 256 {QUICKSORT}. After the OK
has been displayed almost immediately, type {SHOW-ARRAY} again
and you will see that the numbers are sorted from low to high.
Now for the algorithm itself. The word {LARRAY} is an defining
word. It allows to define arrays which can hold 32 bit numbers.
{S-D} is such an array, capable of holding 256 items. With {FILL-
ARRAY} we can fill {S-D} with 256 random numbers. This word uses
the word {RND}, which stands for a random-numbergenerator. In
this case that generator expects two numbers - {RAND} (= 511) and
{I}. The latter indexes into the array. In {SHOW-ARRAY} I lowered
the number of array-elements to 252, because that can be divided
by 12 easily without any modula, to get a nice looking output at
the screen. The following two words bring us closer to the
target. As you will remember from part 12, the returnstack grows
downward into memory. The returnstackpointer always points to the
next free stackspace, so by adding 4 to that pointer it will
point to the last item that was put there and by adding 8 it will
point to the previous element. Notice that 4 and 8 indicate that
this code was ment for a 32 bit FORTH. The words {I'} and {I''}
have been defined to have the opportunity to COPY the two topmost
elements from the returnstack to the parameterstack. The words
{>R} and (R>} REMOVE the topelement from the parameterstack to
place it on top of the returnstack and vice-versa. Let's dive
into the deep world of {QUICKSORT} and examine the phrase:
2DUP >R >R I' I'' + 2/ S-D @. The word {QUICKSORT} expects two
numbers. The smaller number indicates the start- and the higher
the end-element of the array {S-D} you want to be sorted. 3 10
{QUICKSORT} for instance will sort the elements 3 4 5 6 7 8 9 10.
To have the whole array sorted, use 0 256 as the parameters for
{QUICKSORT}. These two numbers are dupped. Then one pair is
removed to the returnstack. From there this pair is copied to the
parameterstack again, added together and divided by two.
THE ESSENCE OF A PIVOT
The stack now holds three numbers 0 256 128. Notice that in this
way we always get a number on top of the stack that is halfway
the two numbers we put there in the beginning. That means that
128 {S-D} {@} will fetch the number that is stored in the middle
element of the array. This number is often referred to as
'pivot-value'. Remember that this is essential for the
quicksort-routine in what language whatsoever. As you can see,
the routine shows three loops starting with the word {BEGIN}.
There is one big {BEGIN...UNTIL}-structure, inside of which
reside two {BEGIN...WHILE...REPEAT}-clauses. The first of these
clauses copies the top of the returnstack (here the number 0) to
the parameterstack to serve as an index into the array {S-D}.
Then the contents of the number zero element is fetched and
compared with the 128th element. If this number zero element is
smaller than the 128th one, we remove the 0 from the returnstack,
add one to it and place it back. With {OVER} we kept 128 on the
parameterstack for further use as will follow. Then we branch
back to {BEGIN}, again we copy the top of the returnstack to the
parameterstack - the top being 1 now !!!!!! -, fetch the contents
of the number one element and compare it with the 128th element
etc. As long as the value in the element we indexed into is
smaller than the pivot-value, that value is allowed to stay where
it is. If not, we will enter the second {BEGIN...WHILE...REPEAT}-
structure. In this structure we act almost in the same way as in
the loop we just left, except we will now compare the rightmost
element (number 256) of the array - found with {I''} - with the
128th one, the pivot-value. If this pivot is smaller - so the
value in the element at the right side of the 128th element
being higher - the rightmost element stays where it is, the
second number on the returnstack is removed to the
parameterstack, 1 is subtracted and 255 is replaced onto the
returnstack. And as long as the value stored in the 255th, the
254th, the 253rd and etcetera element of the array {S-D} is
higher than the pivot, nothing happens and the loop repeats.
But....if that last statement is not true AND the topelement on
the returnstack is not higher than the second element,
(I' I'' > NOT IF), then we will exchange values. Remember, that
we left the first {BEGIN...WHILE...REPEAT}-loop, when the value
in the {I'}th element appeared to be higher than the pivot and
that we left the second loop, when the {I''}th element was
smaller. Notice, that the pivot-value is not likely to be exactly
the middlest value in the range of 0 - 511. It could be any value
in that range, because we stored 256 RANDOMLY chosen numbers into
the successive elements of the array. After the exchange took
place, we increment {I'} and decrement {I''}. This two
comparisons and the exchange of values are repeated until the two
indexpointers cross - {I'} {I''} {>} {UNTIL} i.e. the value of
{I'} grew just one more than the value of {I''}. Then we have to
leave the big {BEGIN}-loop. We drop the pivot value and put the
value of {I'} onto the parameterstack. On that stack we left 0
256 still from the beginning. So the stack shows 0 256 and {I'}.
With {ROT} we put the 0 on top: 256 {I'} 0. Then we remove the
value of {I''} from the returnstack onto the parameterstack. Now
we have gathered there 4 numbers: 256 {I'} 0 {I''}. In this way
we've got as far as {MYSELF} and the game will start all over
again. But...watch the difference: we use only one new starting
value, as we used 0 as a starting value also the first time.
TO STAY OR NOT TO STAY INSIDE !
Let's assume, {I''} had the value of 139, though it might be any
value. The stack will hold 256 {I'} 0 139. Be aware of the fact
that between the zero and the 139th element of the array {S-D}
only numbers smaller than the pivot-value are stored now.
To sort this row with {MYSELF} i.e. {QSORT} we have to choose a
new second pivot-value. Again all elements between zero and 139
are sorted in two rows: a left row containing values less than
the pivot-value, a right row with all the higher values. This
process continues till all left and right rows are sorted
completely. It is obvious, that if the pivot-values happen to
be low numbers again and again, the sorting-time will increment.
But it is not likely, that the pivot-value will be a low number
each time it is chosen. The last word in {QSORT} is {2DROP}.
Because {QSORT} is a recursive routine it should left on the
parameterstack as many parameters as there were in the beginning.
The word {QUICKSORT} will remove this two parameters after the
sorting has been completed. It is striking to see that this
{QUICKSORT} needs much less than 67 calls to {QSORT} to sort the
array. So even a small returnstack can hold all returnaddresses.
It should be worthwhile to investigate the maximum-length of the
array which can be sorted at a given maximum-length of the
returnstack; in other words, when will the returnstack set the
limit. So we are back with the question, WHY the size of the
returnstack determines the depth of a recursive routine and
meanwhile the length of an array to be sorted ? Take a good look
at the definition of {QSORT}. This word is the recursive part of
{QUICKSORT}. The call to {QSORT} with the word {MYSELF} always
occurs from inside {QSORT}. We never leave {QSORT} at {;} to run
it again. The inner live of FORTH now forbids to clear the
returnstack from within a word. Each call to {QSORT} causes a
new returnaddress to be placed on the returnstack. Normally the
size of the returnstack will be large enough to hold all the
returnaddresses. But with a recursive definition it is not
always possible to foresee the amount of calls that it will
make.
KNOWING YOURSELF
To understand the action of {MYSELF}, let's look at its
definition.
: MYSELF ?COMP LAST @ NAME> , ; IMMEDIATE
This definition may vary among different systems. Sometimes it
isn't even there and you will have to define it yourself. The
exact syntax of that definition depends on the system and the
words it provides. But the explanation of {MYSELF} will give you
as much information as you need to be able to define a sort of
{MYSELF} in each FORTH you know about. First of all {MYSELF}
checks if FORTH is in a compiling mood. If not the system will
abort and a message will be displayed. The word {?COMP} may be
left out as well, as it only serves to be sure FORTH is
compiling. Then {LAST} is executed. This word leaves on the stack
the address of the uservariable in which is stored the
namefield-address of the last entry in the dictionary. This is of
course always the namefield of the word in which {MYSELF} is
used. So in {QSORT} this uservariable holds the namefieldaddress
of {QSORT}. With {@} we get this namefieldaddress out of {LAST}
and put it on the stack. The {NAME>} comes into action and
converts the namefieldaddress into the consequent
codefieldaddress, which is compiled into the dictionary by {,}.
So {MYSELF} puts the codefieldaddress of the word it is used in,
into the parameterfield of that word, as if it were one of the
many other words that have to be carried out by that last word.
It is not said here, that the method of recursion which uses
{MYSELF} is the only possible method to program recursive words.
As we shall see when we discuss executionvectors and forward
references, recursive techniques are also made possible by them.
BEING SILLY
Two more 'difficult' words fell: executionvector and forward
reference. Let's start with the explanation of the notion
'executionvector'. So enter this silly word
: FIRSTWORD ." This is firstword " : OK
Now we are going to use {FIRSTWORD} with the following word
: FOLLOWING FIRSTWORD ." used in following." ; OK
Executing {FIRSTWORD} gives
{FIRSTWORD} This is firstword OK
And executing {FOLLOWING} is even more boring
{FOLLOWING} This is firstword used in following OK
As you will remember, it is allowed to redefine a word. So
: FIRSTWORD ." This is redefined firstword" FIRSTWORD is not
Unique OK
will work correctly.
{FIRSTWORD} This is redefined firstword OK
But what about {FOLLOWING} ? Which of the two {FIRSTWORD} will it
use now ? Let's give it a try
{FOLLOWING} This is firstword used in following OK
It's quite clear: {FOLLOWING} stuck with the original
{FIRSTWORD}. It did not switch to the redefined {FIRSTWORD}.
: FOLLOW-UP FIRSTWORD ." used in follow-up " ;
As you are going to see now
{FOLLOW-UP} This is redefined firstword used in follow-up OK
{FOLLOW-UP} used the redefined version of {FIRSTWORD}. Remember
the next rule. Each word that uses another word to define its
action, will use the current version of that other word to
compile into its dictionary-entry. When executed, it will also
use the version of that other word that was current at compile-
time. If after a word has been defined using another word, that
other word is redefined, then the action of that redefined
version will only appear in words defined AFTER the redefinition
of that other word. So if a word has been redefined, then the
original word can't be used any more in new words. Suppose, it is
your deepest and most secret desire to use the old {FIRSTWORD}
into a new word. All money in the whole world would not be enough
to make that dream comes true. But for less than that I will
tell you a secret way-out of terifying dilemma. The simple spell
is called 'executionvector'. And of course FORTH provides all
means to construct this phenomenon. You could see upon an
executionvector as an army sees upon its base. From there
military actions of all kind are carried out in all directions,
but each separate action starts at the base. An example will
clarify things a little. Please enter
: FIRSTTRY ." This is an example." ; OK
: SECONDTRY ." This is another one." ; OK
I SEE..
We now will define a third word, called {YOUSEE}. This word will
have to execute {FIRSTTRY}. We will not define this word as a
direct colon-definition, such as
: YOUSEE FIRSTTRY ;
but we will make the execution work in a more roundabout way. To
execute a word, we have to put its cfa on the stack and then use
{EXECUTE}. It doesn't matter how the cfa was put on the stack.
So, if the cfa was first stored into a variable, we could {@} the
contents of that variable and then use {EXECUTE}. Let's try it
{VARIABLE} VECTOR OK
{'} {FIRSTTRY} {NAME>} {VECTOR} {!} OK
The sequence {'} {FIRSTTRY} finds the pfa of {FIRSTTRY}. Then
{NAME>} converts the pfa to the cfa and with {!} this cfa is
stored into {VECTOR}. We now will define {YOUSEE} as
: YOUSEE VECTOR @ EXECUTE ; OK
Executing {YOUSEE} will write the action of {FIRSTTRY} at your
screen
{YOUSEE} This is an example OK
as could be expected. The point of working in such indirect
manner is that we can simply change what {YOUSEE} does by
changing the contents of the variable {VECTOR}. Just enter
{'} {SECONDTRY} {NAME>} {VECTOR} {!} OK
Now the contents of {VECTOR} is the cfa of {SECONDTRY}. So
{YOUSEE} This is another one OK
will be the action of {YOUSEE}. We didn't have to change the
definition of any word we previously defined, we only had to
change the contents of a variable. With this method you can have
{YOUSEE} execute literally every word in the dictionary, without
changing anything but the contents of {VECTOR}. We have proved
that it is possible to change the action of a word - {YOUSEE} -
AFTER it has been defined, without redefining it. This leads to
the conclusion, that all compiled references to {YOUSEE} will
also change their action. That's because {YOUSEE} will execute
whatever word whose executionaddress is held by the variable
{VECTOR}. This variable {VECTOR} is known as the executionvector
for {YOUSEE}. This facility is very useful, but some problems may
arise with the use of the simple method we described above.
Suppose you forget to store a reasonable address in the variable.
This could cause some spectacular crashes !! There isn't any
check made in this direction. Furthermore - although an Atari has
quite a lot of memory - this method is very inefficient in the
use of memory. And we like to be efficient, don't we ? So most
FORTHs have some alternative method of defining an
executionvector. The main difference between one FORTH and the
other in this case is the name of the defining word which will
create a vectored word. The method is always the same. Therefore
{FORGET} {YOUSEE} OK
{DEFER} {YOUSEE} OK
Now {YOUSEE} has become a vectored word. As long as no action has
been assigned to {YOUSEE}, a default vector to a routine that
will give an error-message becomes active on execution of
{YOUSEE}.
{YOUSEE}
YOUSEE <--- DEFERRED WORD NOT INITIALISED OK
The assignment of an action to {YOUSEE} is quite simple.
{'} FIRSTTRY {IS} {YOUSEE} OK
In this way {YOUSEE} will carry out the action of {FIRSTTRY}. As
with the variable {VECTOR}, it is possible to change the action
of {YOUSEE} by the assignment of another word. Define
: THIRDTRY ." And this is a third action !!" ; OK
{'} {THIRDTRY} {IS} {YOUSEE} OK
Executing {YOUSEE} will execute {THIRDTRY}, but there is a main
difference between {YOUSEE} executing {FIRSTTRY} and {YOUSEE}
executing {THIRDTRY}. In executing {THIRDTRY} {YOUSEE} has been
used as a forward reference. In respect to {FIRSTTRY} {YOUSEE}
carries out a previously defined action and that's quite normal
in FORTH. In the case of {THIRDTRY} {YOUSEE} was defined to carry
out a word that still has to be defined: {YOUSEE} referred
forwardly to a word still to come. The normal compiling process
can only compile the address of a word that has previously been
defined and thus can be found in the dictionary. As we saw above
the use of execution vectors provides a simple solution to the
problem of including a reference to a word that has not yet been
written. A still simpler solution is to define the words in the
correct sequence. But the use of forward references can become a
extremely elegant programming technique in the form of mutual
recursion: two words calling each other. In this case it cannot
be avoided to use a forward reference.
DRAWING A COSY CORNER...
The next example shows what is ment here
NEEDS DRAW LINEA.FTH (1)
GRAPHICS (2)
FORTH DEFINITIONS DECIMAL (3)
6 CONSTANT N 384 CONSTANT H0 (4)
VARIABLE X1 VARIABLE Y1 VARIABLE H (5)
VARIABLE X0 VARIABLE Y0
VARIABLE X VARIABLE Y
: XYPLOT Y1 ! X1 ! ; ( X-coor Y-coor --- ) (6)
: PLOT 2DUP X1 @ Y1 @ DRAW XYPLOT ; ( X-coor Y-coor --- ) (7)
: XYLINE X @ Y @ PLOT ;
: X+ H @ X +! XYLINE ; : Y+ H @ Y +! XYLINE ; (8)
: X- H @ NEGATE X +! XYLINE ; : Y- H @ NEGATE Y +! XYLINE ;
DEFER DRAW-SIDES (9)
: 3SIDES OVER DUP IF 1- SWAP DRAW-SIDES
ELSE 2DROP
THEN ;
: OR1 3 3SIDES X- 0 3SIDES Y- 0 3SIDES X+ 1 3SIDES DROP ; (10)
: OR2 2 3SIDES Y+ 1 3SIDES X+ 1 3SIDES Y- 0 3SIDES DROP ;
: OR3 1 3SIDES X+ 2 3SIDES Y+ 2 3SIDES X- 3 3SIDES DROP ;
: OR4 0 3SIDES Y- 3 3SIDES X- 3 3SIDES Y+ 2 3SIDES DROP ;
4 CASE: (SIDES) OR1 OR2 OR3 OR4 ; (11)
' (SIDES) IS DRAW-SIDES (12)
: INITIALISE H0 DUP H ! 2/ DUP X0 ! Y0 ! ; (13)
: XYSET H @ 2/ DUP H ! 2/ DUP X0 +! Y0 +! X0 @ Y0 @ (14)
2DUP Y ! X ! XYPLOT ;
: PLOT-IT ERASE-SCREEN INITIALISE (15)
0 BEGIN
1+ XYSET 0 3SIDES DUP N =
UNTIL
DROP KEY DROP ERASE-SCREEN ;
This little drawing-routine is a adaptation for the Atari ST from
a FORTH-program on the BBC-computer. The first line checks if
the word {DRAW} has been defined. If not the file LINEA.FTH will
be loaded and compiled. This file contains a FORTH-interface to
the LineA-drawing-routines. {GRAPHICS} initialises these drawing-
routines. Line (3) will place the following words in the FORTH-
vocabulary. Line (4) and (5) defines some constants and
variables. {XYPLOT} at line (6) is a help-word for (PLOT); it
remembers the end-coordinates of a line, so the following line
will start at that point. The four words marked (8) will increase
(X+ , Y+) or decrease (X- , Y-) the screen-coordinates and a
line is drawn from the old to the in- or decreased coordinates.
The lines (9), (10) and (11) form the heart of this program. At
(9) the execution-vector {DRAW-SIDES} is created; as you can see
at (12) it will be assigned to execute {(SIDES)}. In the 'main'
word {PLOT-IT} the execution of {3SIDES} sets the mutual
recursive action at work. The word {3SIDES} - at (10) - will
call {DRAW-SIDES}, which will execute {(SIDES)}, which will
execute one of the four {ORx} words, which will call {3SIDES},
which will call {DRAW-SIDES}, which will call via {(SIDES} one of
the four {ORx} words, which.......etc. You may try of course, but
it will soon become clear, that it is impossible not to define an
execution-vector and yet run this program. This program contains
also a positional case word {CASE:}. It is a defining word. Here
it defines {(SIDES)} to have four cases: 4 {CASE:} {(SIDES)}. It
depends on the value - 1, 2,3 or 4 - which of the four words
following {(SIDES)} will be executed. So 2 {(SIDES)} will execute
{OR2}. By the way, the program is executed by typing {PLOT-IT} on
a b/w-monitor and pressing Return.
EXTRAS
In FORTH DIMENSIONS, a magazine totally devoted to FORTH, an
article appeared with the astonishing announcement, that FORTH is
likely to vanish away as a public known computer-language, due to
the FORTH-programmers, which tend to hide away this beautiful
language from the eyes of the world. In this way, so it was
written, they hope to be the only few people who can make very
fast a very fast computer-application. So don't let it happen !
EXERCISES
1. Introducing the word {?THOUSAND}, I told you not to execute
the definition as it might cause a hang-up. I also gave you
one of the possible reasons: the returnstack. Now, could you
find a solution to this problem by changing the definition ?
You may change either one or two of two items.
2. In which part of the array {S-D} have been stored the values
less than the pivotvalue, left or right of that value, when
{I'} crossed {I''} for the first time ?
3. We made the presumption, that - at a certain moment - {I''}
had the value of 139. What now must have been the value of
{I'} at the same time ?
4. Could you tell me which element served as the second pivot-
value, assuming {I''} equals 139 ?
5. In which part of a dictionary-entry of a new word FORTH will
compile another, already existing word, to define part of
the action of the new word ? Is it the linkfield, the
parameterfield or the namefield or none of the three ?
6. It is possible to define a word which can view the action of
{PLOT-IT} step by step. Write it and call it {STEP} !
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.