[comp.lang.forth] Style

ir230@sdcc6.ucsd.edu (john wavrik) (08/03/90)

Wil Baden has started a discussion of Forth style. I dug out the 
following example which I once used in a Forth course. The task is 
simple enough that it can be easily understood -- and difficult enough 
so that style matters. It was chosen so that the issue would be coding 
(rather than improvement in algorithm, knowledge of math, etc.)

TASK:  Write a program in which the computer uses binary search to 
       guess a user's "secret" number.  The user thinks of a secret 
       number, an integer from 1 to 100. The computer makes a guess 
       to which the user must respond by typing LOW, HIGH or RIGHT. 
       This is repeated until the computer guesses the number.

I don't remember what version of Forth we were using when this was 
written -- but it shouldn't matter unless you plan to actually run it. 

We used the following words from a string package (all strings are 
counted with a 1-byte count and are represented by their address):

$VARIABLE    ( n -- )  defining word for string variables.
             <n> $VARIABLE X  allocates storage for a counted string 
             of length n. When X executes the address of the string is 
             put on the stack.

$"           ( -- $addr ) the following text up to a delimiting " is 
             stored as a counted string. The address of the string is 
             put on the stack.

IN$          ( -- $addr ) accept a string from the keyboard until <cr>.
             The input is stored as a counted string whose address is 
             put on the stack.

$COMPARE     ( $1 $2 -- -1|0|1 ) compares counted strings. Returns -1 
             if $1 comes before $2 in lexicographic order, 1 if it 
             comes after, and 0 if the strings are equal.

$!           ( $ addr -- ) copy counted string at address $ to addr.
             Typical usage  $" HELLO" X $!  where X is a string 
             variable.


Here is a solution to the problem which shows why some people think 
Forth is a write-only language: 
             
( GUESSING GAME PROGRAM                                 1  )
10 $VARIABLE A
:  GO  BEGIN 0 101 BEGIN  2DUP + 2/  DUP  CR ." I GUESS "  . CR BEGIN 
." IS THAT HIGH, LOW, OR RIGHT " IN$ A $! A $" HIGH" $COMPARE  0=  IF 
SWAP DROP 0 1 ELSE A $" LOW" $COMPARE 0= IF ROT DROP  SWAP  0 1 ELSE A 
$" RIGHT" $COMPARE 0= IF CR ." I  GOT IT!!" DROP DROP DROP BEGIN CR ." 
PLAY AGAIN (Y OR N)" KEY DUP 78 = IF DROP  1  1 1 1 ELSE 89 = IF 0 1 1 
1 ELSE 0 THEN  THEN  UNTIL ELSE CR 0 THEN THEN THEN UNTIL UNTIL UNTIL ; 

( Why spend hundreds of dollars for a program to obfuscate your source 
code?  You can do it yourself for less in Forth! )

Actually, something like this can be done in any language. I did it by 
removing the indentation from the following program. The following is 
bad style also. It is a transcription to Forth of a program which the 
student originally wrote in Pascal (this stuff comes from several 
years back when Pascal was the "in" language). I don't think this is 
what Wirth had in mind when he invented Pascal -- but it seems typical 
of the way many people write Pascal and 'C': 

( GUESSING GAME PROGRAM                                 2A )
10 $VARIABLE ANSWER
: GAME   BEGIN 0 101
          BEGIN  2DUP + 2/  DUP
                 CR ." I GUESS " .  CR
               BEGIN  ." IS THAT HIGH, LOW, OR RIGHT "
                      IN$  ANSWER $!
                      ANSWER $" HIGH" $COMPARE 0=
                      IF   SWAP DROP 0 1
                      ELSE ANSWER $" LOW" $COMPARE 0=
                        IF   ROT DROP SWAP 0 1
                        ELSE  ANSWER $" RIGHT" $COMPARE 0=
                            IF CR ." I GOT IT!!"  DROP DROP
                               DROP
                               BEGIN CR ." PLAY AGAIN (Y OR N)"
                                     KEY DUP 78 =    -->

( GUESSING GAME PROGRAM                                 2B )
                                     IF   DROP 1 1 1 1
                                     ELSE  89 =
                                        IF  0 1 1 1
                                        ELSE 0
                                        THEN
                                     THEN
                               UNTIL
                           ELSE  CR 0
                           THEN
                        THEN
                      THEN
               UNTIL
          UNTIL
        UNTIL ;

[I notice in reproducing it that the programmer forgot to deal with the 
case in which the user types in something other than HIGH, LOW or 
RIGHT.] 

The guessing game program is logically more complex than it might 
first appear. You might want to see what you can do with it before 
reading the code below. 

=======================================================================


( GUESSING GAME PROGRAM                                 3A )
VARIABLE LL   VARIABLE HH   ( low and high for current range )
VARIABLE GG                 ( current guess )

: GUESS  CR  ." I guess "
        LL  @  HH  @  + 2/     ( guess  midpoint  of  range  )
        DUP    GG   !    .     ( and store guess  )
        ." is that LOW, HIGH, or RIGHT" ;

: START  0 LL !  101 HH !  GUESS  ;

:  HIGH    GG @ HH !  GUESS   ;  ( take lower half of  range )
:  LOW     GG @ LL !  GUESS   ;  ( take upper half of  range )
:  RIGHT   CR ." Wow, I got it!!"  ;

In this version, the complexity of nested control structures has been 
elminated by factoring. The Forth interpreter has been made part of 
the game (LOW and HIGH are not strings decoded by the program -- but 
are now Forth words). The low and high bounds and the guess were put 
in variables rather than left on the stack: if the user puts in a bad 
response, the Forth interpreter will trap the error -- but also will 
clear the stack. Putting the numbers in variables allows the game to 
continue after an error. 

While I won't claim that this version is the ultimate, I do feel that 
someone can read the code and understand what is going on. I do not 
feel this is true of either of the earlier versions.

======================

To Wil Baden and others who may be interested in this:

    I recently was asked by someone why I was using Forth to do work 
in Computer Algebra rather than Scheme or a Computer Algebra system 
(like MACSYMA, Maple, Mathematica, etc). I answered that, among other 
things, I feel that it is easier to write clear code in Forth. To 
prove my point, I found a computer algebra project for which I have 
code written "by the masters" (by Ableson and Sussman^2 in Scheme, and 
by David Stoutemyer in MuSIMP -- the implementation language for the 
MuMATH computer algebra system he created). In both cases the authors 
were explaining to students how to do the project -- so I must assume 
they tried to make their work a model of clarity. I think that the 
improvement of the Forth version over the other is substantial. The 
difference would be visible to anyone who knows the three languages 
and a bit about symbol manipulation. [The comparison is too long to 
post, but I would be willing to email it -- and I would welcome 
detailed comments by others who are interested in using Forth in 
mathematics] 

   I believe that, potentially, Forth can be quite clear -- just as I 
think, potentially, that Forth can be quite portable. In both cases 
the barriers are not in limitations of the language. 

                                                  John J Wavrik 
             jjwavrik@ucsd.edu                    Dept of Math  C-012 
                                                  Univ of Calif - San Diego 
                                                  La Jolla, CA  92093