A tomato a day keeps
the doctor away.
My mother-in-law : Collected Works.
STRENGTH IN FORTH
part five
MAKING A TRUE DECISION (2)
It's rather easy for me now to complete this part about loops and
conditional structures. That's because I can restrict myself to
two words: {BRANCH} and {0BRANCH}. These two elements are used to
implement structures as {IF..ELSE..THEN}, {BEGIN..UNTIL},
{BEGIN..WHILE..REPEAT} and {BEGIN..AGAIN}. The word {BRANCH} is a
unconditional branch. It always occurs, if that point (see
beneath) is reached. {0BRANCH} is only activated if the condition
is FALSE. You can find {BRANCH) and {0BRANCH} in the dictionary.
One feature all loops - the definite {DO..LOOP} as well as the
conditional {IF..THEN}-structure and the indefinite loops - have
in common: they should only be used from within a definition !!!
It's for sure, that control-structures as indefinite loops and
conditional branches can produce tidier applications than definite
loops and jumps out of loops.
CERTAIN CONDITIONS
The first (conditional) structure to be explored will be
IF..(THEN)..ELSE. Here is its scheme:
0BRANCH
|¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯|
{IF} {ELSE} é {THEN}
|________BRANCH______
In BASIC this control-structure is used as IF..THEN..ELSE. This
could be read as: IF condition=TRUE THEN do-this ELSE do-that. In
FORTH it's quite different read as: condition IFTRUE do-this IFNOT
do-that THEN_GO_ON with-the-following. So if 'condition' is TRUE
do-this will be executed, followed by with-the-following. If the
condition is FALSE, do-that will be executed, followed by with-
the-following. In both cases with-the-following is executed. In
FORTH first the condition is tested i.e. FORTH examines the value
TOS on being zero or non-zero, FALSE or TRUE respectively. First
we'll have a closer look at a plain IF..THEN-structure. a) If TRUE
the words between {IF} and {THEN} are executed. Then execution
goes on with the words after {THEN}. b) If FALSE the words between
{IF} and {THEN} are omitted and a direct branch is made to the
words beyond {THEN}.
When {ELSE} is used with {IF}..{THEN}, the words between {IF} and
{ELSE} are executed on a TRUE-test and when execution arrives at
{ELSE} the unconditional branch is made to {THEN}. When {IF} finds
zero TOS a branch is made beyond {ELSE} and the words between
{ELSE} and {THEN} are executed, followed by the words after
{THEN}.
As you might have guessed it's allowed to nest IF..(ELSE)..THEN_
structures. The nesting is submitted to the same rules as apply to
a DO..LOOP. When the nesting grows very complicated, you should
see, wether your definition could be splitted in smaller ones, or
you should write down your definition on a piece of paper like
this: IF(1)....ELSE(1)..IF(2)..ELSE(2)...THEN(2)...THEN(1). But I
prefer the smaller-ones-technique. Like this barking example.
First we define a variable. VARIABLE ?DOG OK : IFSO 1 = IF ." Run
for your life, if you have one !! " THEN ; OK : IFNOT 0= IF ." Go
inside and say 'Hello' " THEN ; OK : INIT ?DOG ! ; OK : SAFE 0
?DOG ! ; OK. Now we can construct the final word. : DOG? INIT
?DOG @ DUP IFSO IFNOT SAFE ; OK. Execute the DOG?-word with one
parameter (1 or 0). 1 DOG? Run for your live, if you have one !!
OK. We could have defined DOG? as: : DOG? DUP 1 = IF ." Run for
your live, if you have one !! " ELSE ." Go inside and say 'Hello'
" THEN DROP ; . While discussing the relational operators, I
mentioned the fact, that conditional branches like
IF..(ELSE)..THEN in most cases, make their tests with help of
these operators. Consider my mother-in-law. She has dedicated her
life to grow tomatoes. (And grapes, to make some wine for me).
Enchanting, isn't it ? She really grows the tastiest tomatoes I
ever ate. And all sizes. Imagine she had a machine to sort the
tomatoes according to their sizes. I could have automated this
process with FORTH and some electronics. She hasn't got such a
machine, so I'm alas restricted to make a text-version of it.
Let's sort all tomatoes into 5 groups: EL, L, M, S, ES. That is
from extra large to extra small, according to their weight.
: TOOSMALL DUP 25 < IF ." Rejected " THEN ; OK
: ?ES DUP 50 < IF ." Extra small" THEN ; OK
: ?S DUP 125 < IF ." Small " THEN ; OK
: ?M DUP 200 < IF ." Medium " THEN ; OK
: ?L DUP 250 < IF ." Large " THEN ; OK
: ?EL DUP 250 > IF ." Extra large" THEN ; OK
It might seem very simple now to forge these small ones together
into one final definition : SIZE TOOSMALL ?ES ?S ?M ?L ?EL DROP ;
OK, to determine the size of a tomato. Try some numbers !! It
doesn't work very well, does it ? We will have to look around for
some better way. The smaller-ones-technique failed in the case of
the tomatoes. The nesting method has to bring the solution here.
But that one will have to wait for the touch of YOUR magic 'green'
fingers in the exercises.
ALL BEGINNING HAS AN END
Here is another one.
|¯¯¯¯¯¯0BRANCH¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
{BEGIN} {WHILE} {REPEAT}
ü |
|__________________BRANCH_____________________|
Now, if you examine the scheme above well, you will undoubtedly
notice, that it is a variation of IF..ELSE..THEN. The {IF} is, as
it were, part way into the loop. The 0BRANCH now starting from
{WHILE}, if the condition between {BEGIN} and {WHILE} is FALSE.
And it swings us out of the indefinite loop far beyond (REPEAT}.
If the condition preceding {WHILE} is TRUE, the words between
{WHILE} and {REPEAT} are executed. When execution encounters
{REPEAT} an unconditional BRANCH is made backwards to {BEGIN},
where it all starts again. An example to honour the fleet, my
MAJESTY's or your MAJESTY's...it doesn't matter. VARIABLE SHOT OK.
: HIT BEGIN 1 SHOT +! SHOT @ DUP 10 < WHILE . ." hit is no
hit. We rule the waves !!" REPEAT . ." hit is a real hit.
Blubblubblub....." ; OK. As you can see in this example of courage
and arrogance, the TRUE-FALSE-test has to precede {WHILE}
immediately, although other words may be injected between {BEGIN}
and {WHILE}. The {+!}-word resembles {!}. In fact, it also stores
values at addresses. Unlike {!} however, it doesn't replace the
old value with a total new one, but increases the old value with
the given number. 123 SHOT {!} will store the value 123 at the
address SHOT (the parameter field address, remember ?). Having
done that job, 456 SHOT {+!} will add the value 456 to the old
value 123 at the address SHOT. The variable SHOT will now contain
the value 123 + 456 = 579. Now execute HIT.
RIDICULOUS
Two indefinite loops remain to be explored: the {BEGIN}..{UNTIL}-
and the {BEGIN}..{AGAIN}-loop. They are - at branch-level - each
other's counterpart. See for yourself.
鯯¯¯¯¯¯¯¯0BRANCH¯¯¯¯¯¯¯¯¯¯¯¯|
{BEGIN} {UNTIL}
鯯¯¯¯¯¯¯¯¯BRANCH¯¯¯¯¯¯¯¯¯¯¯¯|
{BEGIN} {AGAIN}
As you could have figured out for yourself, the testing on
TRUE/FALSE takes place at {UNTIL}. At {AGAIN} no test is made as
the branch (backwards) is unconditionally carried out. The words
between {BEGIN} and {UNTIL} are executed as long as the test made
by {UNTIL} is FALSE. This test has to precede {UNTIL} immediately.
If the test gives TRUE, the words after {UNTIL} are executed. It's
easy to see, that the {BEGIN}..{AGAIN}-loop is for ever lasting.
That may seem ridiculous, but it is not. The main routine in each
computer is such an endless loop. In the FORTH-system it is used
for the keyboard interpreter, which interprets all input to the
computer. While FORTH is in action all operations are at a more or
less deep level of nesting from within that keyboard interpreter.
Ultimately control must return to him, displaying OK. So it is
very usefull for turnkey applications, where the user is not
expected to know or wish to alter the method of operation. Here is
an example of a {BEGIN}..{UNTIL}-loop. A slightly usefull one.
: G-C-D BEGIN ( n1 n2 -- gcd )
SWAP OVER MOD ?DUP 0=
UNTIL
;
: .GCD ( n1 n2 -- gcd )
G-C-D CR ." The GCD is " .
;
Entering 12 20 .GCD will display The GCD is 4 OK. GCD is of course
the greatest common divisor. In this two definitions another
feature of FORTH-programming is beautifully made visible. The
display-routine and the actual calculation-routine are placed in
separate words. Why ? Well, if in a larger application the GCD is
to be displayed as well as merely calculated without displaying
the result, we can use the G-C-D-word in both cases. It saves
rewriting the calculationroutine. There are some conventions in
naming words too. A dot as first character indicates that the word
will print something, as .GCD does. If a routine has to store some
value it is often named !PIPO (Pipo is a dummyword). When a word
has to give TRUE or FALSE on some action of the user ? is used as
an indication of the word's nature: ?KEY, was a key pressed or not
pressed ? It's another help to make a program more readable to
another human (and to yourself). The efforts layed in a well-
structured lay-out, a well-chosen name and well-proportioned words
pay ten times back as you will enjoy clear structures as the
result of clear thinking........... Well, it was easy after all !!
A WARMING-UP
Now we have some room to take the returnstack out of the freezer.
After a warming-up, it appears as a ordinary stack....however it
is not, as you should be well aware of. The returnstack is used
for system control. One, just one uncontrolled movement on this
stack is disastrous. Your system will go away. Nevertheless the
returnstack can be accessed by the user of FORTH. We've done it in
the {DO..LOOP}, using {I} and {J}. Both transfer numbers from the
returnstack to the computationstack. A further three words earn a
living transferring numbers to and fro. Here they are: {R@} {>R}
{R>} (say: are-fetch, to-are, are-from). {>R} and {R>} are
inseparable twins. {>R} transfers the top item of the
computationstack to the returnstack, {R>} the top item of the
returnstack to the compu-stack. In both cases the items are moved
from their respective stacks. ( {I} and {J} just COPY items, not
MOVE them ). As {>R} and {R>} modify the contents of the
returnstack, they should never be used directly from the keyboard.
Always use them within a definition. And within a definition
always (normally) as a pair. Maintaining this simple rules will
ensure that the state of the returnstack is unchanged between
entry and exit, when the word containing {>R} and {R>} is later
executed. Are those two words that important then ? No, not
really. Their main use is as a temporary store for the top item on
the compu-stack, when the items below are needed for some reason.
Let's step in reality. Suppose the stack shows like this:18 23 34
55 and you would have to multiply 18 and 23 and then divide the
result through 34. That needs some shuffling on the stack !! With
the help of the twins it's easily done. But within a definition !!
: TWINS 18 23 34 55 >R */ R> .S ; On executing TWINS it goes
like:
STACK WORDS COMMENTS
18 23 34 55 >R 55 is transferred to the ret-stack
18 23 34 */ 18 * 23 / 34 (star-slash-action)=12
12 R> restore 55 on the compu-stack
12 55 .S print the stackcontents
Now the stack is showing 11 18 23 55 34 66. The numbers 18 23 34
will play the same rôle. The extension is that you have to add 11
and 55 and to subtract 66 from the result. It's done by TWINS2. :
TWINS2 11 18 23 55 34 66 >R SWAP >R */ R> ROT + R> - .S ; OK.
Figure out for yourself how TWINS2 is doing the job, just like I
did with TWIN. As you can see, every {>R} has its {R>} and as long
as that is the matter within the same definition, it's allright. I
almost forgot {R@}. This word copies the top of the returnstack to
the computationstack. It does not alter the returnstack. Imagine
that you have to add a constant changing number to 5 different
numbers. Here is the word that will perform the required action.
: +5DIF >R R@ 10 + . R@ 11 + . R@ 12 + . R@ 13 + . R@ 14 + . >R
DROP ; OK. 10 +5DIF will give 20 21 22 23 24 .
A VARIABLE SUBJECT
Scattered all around through this course are remarks on {CONSTANT}
and {VARIABLE}. Let's scratch these together and add some fresh
ones. Now {CONSTANT} and {VARIABLE} are both defining words. They
can create a new entry in the dictionary. It's quite obvious from
their names, what kind of entry that will be. The word {CONSTANT}
is used to name and number a value that won't change at run-time
of an application. It is used as 10 {CONSTANT} VAT. (VAT is
British BTW, MwSt). In BASIC you would have written VAT=10. The
word VAT on execution places the value 10 TOS. Let's assume VAT is
just a tax of 10 percent. The calculation of a price incl. VAT
would go like this. : PRICE DUP VAT 100 */ + . ; Here is what
happens, when you type 150 PRICE 165 OK. So 150 was excl.,165 is
incl. VAT.
STACK WORDS COMMENTS
150 DUP Duplicating 150
150 150 VAT The value 10 goes TOS
150 150 10 100 100 goes TOS
150 150 10 100 */ 150 * 10 / 100 = 15
150 15 + 150 + 15 = 165
165 . Prints the result
We could have nòt defined a constant and simply have used the
value of 10. Of course we could have - and it is better practice
in small applications of one or two words - , but when an
application involves a huge amount of calculations you better
define a constant. For two reasons. First of all the government
doesn't yet know of that 10-percent-VAT, but what if she knew ?
Yes, she would increase it and you would have a nice job, changing
10 to 20 everywhere 10 appears in your program. Now if you would
have defined a constant VAT, you only would have had to change 10
{CONSTANT} VAT in 20 {CONSTANT} VAT and everywhere in the
application VAT would have produced 20. Secondly, VAT has more
sense than 10 (or 20). Remember: the value of a constant is set at
its creation. Why should you alter the value of a CONSTANT ? I
don't know. It can be done yet. Like this: 20 {'} {VAT} {!}. OK .
Typing {VAT} {.} gives 20 OK . The word {'} (say: tick) places the
parameter field address of {VAT} TOS. Beneath that pfa of {VAT} is
the number 20. {!} takes the second item on the stack, assuming
it's a number and stores it at the TOS-item, assuming that's an
address. Sometimes it happens, that the value of a constant
depends on the value of (an)other one(s). It's a good practice to
then calculate the dependant constant. An example will clarify
this:
2000 {CONSTANT} SCREEN
80 {CONSTANT} COLUMNS
SCREEN COLUMNS / {CONSTANT} LINES.
The constant SCREEN is worth 2000. The constant COLUMNS is worth
80. The constant LINES is consequently 2000 / 80 = 25. Any change
made in SCREEN and/or COLUMN will affect LINES' value.
A {VARIABLE} is declared like this: {VARIABLE} CHANGE. You see, no
initialising a value for {CHANGE}. In some FORTH-dialects, you
have to initialise a variable at its creation: 0 {VARIABLE}
ANOTHER. I mention it here, because some books on FORTH write
about it. It's easy to write a word that initialises a variable to
zero. : VAR CREATE 0 , ; OK . You use it like this: {VAR} ME OK
{ME} has got the value 0. Initializing a variable with any value
is made possible by this definition: : VARIANT CREATE , ; OK.
The use is 23456789 {VARIANT} ONOMATOPEE OK. {ONOMATOPEE}'s value
is 23456789. {ANOTHER}'s {VARIABLE} is constructed like {VARIANT}.
'Our' {VARIABLE} can be defined in high-level FORTH comme-ça; :
VARIABLE CREATE 4 ALLOT ; and just for fun the definition of
{CONSTANT} in high-level FORTH: : CONST CREATE , DOES> @ ;. It
really works !! You use it like {CONSTANT}.
I introduced the words {CREATE} and {DOES>} and the tiny word {,}.
The latter compiles a number in the first available free
dictionary-space. {CREATE} and {DOES>} are very powerfull words.
I told you, that FORTH lets you extend his dictionary. You can
write words and add these to the dictionary. But FORTH gives you
still more power. I also told you, that {CONSTANT} is a defining
word; it compiles a dictionary entry, just like {:...........;}.
As you can see, the defining word {CONSTANT} was built with
{CREATE....DOES>}. So you can also define words, which can define
new types of words, as I did with three types of {VARIABLE} !!!
More on that subject (much)later.
The word {ALLOT} allots memory-space, as many bytes as the number
preceding it indicates.
Now, how to put some value in a variable ? Use {!}. The format
is 123 {CHANGE} {!}. You can change that value by storing a total
new value, 234 {CHANGE} {!}, or by increasing/decreasing its value
with {+!}. Increasing goes like this: 66 {CHANGE} {+!}, which
brings 300 for {CHANGE} (234 + 66 = 300). Decreasing follows this
method: -66 {CHANGE} {+!} and {CHANGE} is back to 234 again. To
get the variable's value onto the computationstack {@} is used:
{CHANGE} {@} . 234 OK. Don't use {.}, if you didn't intend to
display its value. For displaying a variable's value is done in a
easier way with {?}: {CHANGE} {?} 234 OK. Of course {?} is defined
in this way: : ? @ . ; Simple ? Yes, very !!
Now we discussed three subjects. The remaining part about loops,
the returnstack and constants and variables. Next time we'll have
a closer look at input and output in FORTH.
Till then !!
SUMMARY
The conditional loopstructure in FORTH is the {IF...THEN}-loop.
The syntax is somewhat different from BASIC's IF..THEN. In FORTH
it's layed out like this: 'condition' IF 'words' THEN. If the
condition is TRUE, the words between IF and THEN are carried out,
if FALSE these words are skipped and control is handed over to the
words, if any, following THEN. This conditional structure can be
extended with {ELSE}, placed between {IF} and {THEN}. If the
condition is FALSE, the words after {ELSE} are executed, followed
by the words after {THEN}.
There are three indefinite loops. {BEGIN..WHILE..REPEAT}, {BEGIN..
UNTIL} and {BEGIN..AGAIN}. The respective syntacti are: {BEGIN
(words) 'condition' WHILE words REPEAT}, {BEGIN words 'condition'
UNTIL}, {BEGIN words AGAIN}. The 'conditions' are tested by
{WHILE} and {UNTIL}. As long as 'condition' is TRUE the words
between {WHILE} and {REPEAT} are executed. As long as 'condition'
is FALSE the words between {BEGIN} and {UNTIL} will be performed.
All loops and conditional structures should only be used from
within a definition. All loops, definite and indefinite, and
conditional branches may be nested mutually, provided that each
component is fully enclosed by another.
The returnstack can be used by the user for temporary storage of
Parameters and numbers. It can only be done from within a
definition by use of the words {>R} and {R>} in numerical
conjunction: 2 times {>R} requires 2 times {R>} etc. The word {R@}
copies the item on top of the returnstack onto the top of the
compu-stack.
{CONSTANT} and {VARIABLE} are defining words. These can create a
dictionary-entry. They are used to declare programm-constants and
-variables. The initial value of a constant is given at its
creation and is usually maintained throughout the application. The
value of a variable is stored at a later point in the application
by means of {!}. This value can be changed by {!} and {+!}. On
execution of a constant its value is placed TOS. On execution of
a variable its address is placed TOS, its value can be fetched
from the address with {@}.
REMEMBER
Branch - In an application a branch occurs, when control no
longer follows the common course , but leaves it to
fulfill a task elsewhere in memory. The application
may come to an end there or it may return to its
common course and go on. A branch may be backwards or
forwards. A branch may occur, if certain conditions
are fulfilled or it may be forced. These are called
conditional and unconditional branches respectively.
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| WORD | STACKACTION | DESCRIPTION |
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| IF | (f --- ) |Used in colondefinitions |
| | |in the forms: IF (true)..|
| | |..THEN and IF (true).....|
| | |ELSE (false)....THEN. |
| THEN | |See IF. Marks the desti- |
| | |nation of forward bran- |
| | |ches from IF or ELSE. |
|______________|_______________________|_________________________|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| WORD | STACKACTION | DESCRIPTION |
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| ELSE | |See IF and THEN. |
| >R | (n ---) |Moves a number from the |
| | |compu-stack to the return|
| | |stack. Must be balanced |
| | |with R>. |
| R> | (--- n) |Moves the top number from|
| | |ret-stack to the compu- |
| | |stack. See >R. |
| R@ | (--- n) |Copies the top of the ret|
| | |stack to the compu-stack.|
| +! | (n addr ---) |Adds n to the value at |
| | |address addr. |
|______________|_______________________|_________________________|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| WORD | STACKACTION | DESCRIPTION |
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| BEGIN | |Used in a colondefinition|
| | |in the forms: BEGIN......|
| | |AGAIN , BEGIN...UNTIL and|
| | |BEGIN..WHILE..REPEAT. |
| | |BEGIN marks the start of |
| | |a repeatedly executed se-|
| | |quence and acts as a |
| | |returnpoint from AGAIN, |
| | |UNTIL and REPEAT. |
| AGAIN | |Forces a branch back to |
| | |BEGIN, creating an end- |
| | |less loop. See BEGIN. |
| UNTIL | (f ---) |If f is FALSE execution |
| | |branches back to BEGIN,if|
| | |TRUE execution goes on |
| | |with the next word after |
| | |UNTIL. See BEGIN. |
|______________|_______________________|_________________________|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| WORD | STACKACTION | DESCRIPTION |
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| WHILE | (f ---) |On f=TRUE execution con- |
| | |tinues on REPEAT, which |
| | |causes a branch back to |
| | |BEGIN. On f=FALSE execu- |
| | |tion skips to the word |
| | |next to REPEAT. See BEGIN|
| REPEAT | |In execution REPEAT for- |
| | |ces an unconditional |
| | |branch back to BEGIN. See|
| | |BEGIN. |
| ALLOT | (n ---) |Reserves n bytes of dicti|
| | |onaryspace, increasing |
| | |the dictionarypointer |
| | |with n. See later DP. |
| ? | (addr --- ) |Prints the value at |
| | |address addr. |
| ' | (--- addr) |Gives pfa of the ticked |
| | Used as: ' word |word if in executionmode |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
EXTRAS
As one of the pecularities of FORTH, I mentioned the extensibility
of this marvellous language. You can extend the executable words
in the dictionary, you can extend furthermore the compiler by
creating new defining words, which can write new executable words
in the dictionary. The third way of extending FORTH is producing
alternative ways of generating new defining words. This process is
often referred to as Meta-FORTH. It enables the writing of new
FORTH-like languages !! I own a Meta-FORTH, but it's damaged !!
Meta-FORTH is far beyond the scope of this course and......my
knowledge, honestly.
EXERCISES
1. Look upon the smaller definitions, which make out the {SIZE}-
word, as separate definitions. One of them is superfluous, if you
make some minor changes in another one. Which one is superfluous
and which one is to be changed and how ?
2. Why can't you simply substitute the changed definition in the
'old' {SIZE}-word to form a new {SIZE-2}-word ?
3. Define a new {SIZE-3}-word, using nested {IF..ELSE..THEN}-
structures. The {SIZE-3}-word should work better than the {SIZE}-
word.
4. Consider the following. VARIABLE EL OK ; 0 EL ! OK ; : ?EL
DUP 250 > DUP IF EL +! ." Extra large" THEN EL @ . ; Execute the
new ?EL-word: 300 ?EL. See what happens. Now rewrite the {SIZE}-
word, that not only the tomato-sizes are printed out, but also
their respective quantities. Take care of a good-looking display
with each size on a new line.
5. The next exercise, thanks my mother-in-law, is to write a
word, which results in a display like:
Extra large 8 tom. 2 kg
Large 20 tom. 5 kg etc.
Now, as the sizes are within certain limits of weight, e.g. large
is within 200 and 250 gr, you should calculate the number of kg's,
using the lower limit. The overweight as a result of tomatoes not
all being as heavy as exactly 200 gr is not to be taken in
account. My mother-in-law is a very generous woman !! And don't
give up too soon !!!
6. If you succeeded in defining the word asked for in the
previous exercise, you did a great job !! To function properly on
a machine which has to carry out the tomato-sorting continuously,
this word has to be extended from a software point of view. Which
extension is meant here ?
SOLUTIONS TO PART 4
1. The (CLS)-word is NOT a defined word i.e. you must find out on
your system, how to clear the screen.
: LINE (CLS) SWAP 0 DO CR LOOP SPACES ;
2. : ASC1 (CLS) DO I EMIT LOOP ;
3. : ASC2 (CLS) DUP ROT 20 MIN + SWAP DO I EMIT LOOP ;
4. : POWERLIFT SWAP DO I . I +LOOP ;
5. : BACK DO I . -1 +LOOP ;
6. We define the powerraising routine first.
: ^ 1- ?DUP IF OVER -ROT 0 DO OVER * LOOP SWAP DROP THEN ;
If your system lacks {-ROT}, use {ROT} {ROT}. Next we define the
word {THICK}. : THICK 2 25 ^ 55 + 100 / CR ." The paper is " .
." mm thick now !!" ;. Notice how the rounding is performed (55
+). Dividing by 100 is the same as multiplying with 0,01. If you
want to observe the folding step by step you should define {THICK}
as : THICK 2 SWAP ^ 55 + 100 / CR ." The paper is " . ." mm thick
now !!" ;. Then enter 18 THICK for instance. The word {^} can be
used separatedly as: 8 11 {^}, this will calculate 8^11.
7. The solution to make 1 I gave as an example. : TWO 5 5 / 5 5 /
+ . ; : THREE 5 5 5 + + 5 / . ; : SIX 55 5 / 5 - . ; :
SEVEN 5 5 + 5 / 5 + . ; : NINE 5 5 + 5 5 / - . ; : TEN 5 5
5 + + 5 - . ;. It's possible to make 4 and 5 as well. But that's
for some other time !!
8. One can't dig half a hole. It may be small, but a whole !!
9. : TABLE 11 1 DO CR DUP DUP I . ." X " . ." = " I * . LOOP
DROP ;. In the next part I'll teach you how to perfect the output.
10. : SAME 9 MIN 10 0 DO DUP I = IF ." Equal " I . LEAVE THEN
LOOP ;
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.