[net.lang.forth] Forth and recursion

ma168x@sdcc3.UUCP (John Wavrik) (05/03/86)

Re:  Posting on recursive computation  (B.D. Szablak)

  Fibonacci numbers provide an excellent example of a situation
in which iteration should be used rather than recursion.
              FIB(n) = FIB(n-1) + FIB(n-2)
       with   FIB(0) = FIB(1) = 1
It is easy to see that the number of additions required grows
like n if iteration is used and like FIB(n) if recursion is used

   A recursive computation of FIB(20) requires 10945 additions
while an iterative computation only requires 19 additions.

ITERATION:
For Fibonacci numbers, let F be a variable holding the most
recent Fibonacci number and F- the one before.

   : FIB  ( iterative )   DUP 2 <
         IF        DROP 1            ( boundary value)
         ELSE   1 F- !  1 F !
                1 DO    F- @  F @     DUP F- !  ( update F-)
                        +                 F  !  ( update F )
                  LOOP   F @
         THEN  ;

RECURSION:
Forth is implemented deliberately to prevent the word currently
being defined from being found in a dictionary search.
There are three major techniques used for recursion in Forth:
     1.  Defeat the (system dependent) method used to
         prevent self-reference [e.g. toggle the SMUDGE bit].
     2.  Provide a word (usually called MYSELF) which compiles
         the code address of the current word.
     3.  Use execution vectors.

Here is an example using approach #2:
  : FIB  ( recursive )  DUP 2 <
         IF    DROP 1
         ELSE  DUP 1-  MYSELF  SWAP  2-  MYSELF +  THEN  ;

The FIG-Forth definition of MYSELF is
  :  MYSELF  LATEST  PFA CFA ,  ; IMMEDIATE
   ( where : LATEST   CURRENT @ @  ;  )

Recursion is not a major control mechanism in Forth. It should
be noted that most Forth systems have relatively small return
stacks -- limiting the depth of recursion to 100 levels or less.
This is adequate for isolated situations (e.g. quicksort) where
recursion is justified.
   Forth is a very interesting language: the system is open and
the method of implementation simple. It soon becomes evident
that it would be very easy to provide for all kinds of things:
to use self-modifying code, to define a GOTO word to jump from
one word to the middle of another, etc.  Support for these is
not provided and the possibility not mentioned in books. There
is a good reason:  Forth was developed when efficiency and the
ability to write maintainable code were important.
   Recursion is borderline. It is often not efficient but can
sometimes be useful. Many Forth systems provide it and it can be
easily added to others.
                                      John J Wavrik
                                      Dept of Math
                                      Univ of Calif - San Diego
...ucbvax!sdcsvax!sdcc3!ma168x

mj@myrias.UUCP (Michal Jaegermann) (05/06/86)

From responsen on the net seems that I better post that for a wider
audience.  Here are excerpts from an article which I wrote for a
newsletter of a users' group.  It just happened that I tried to show
some methods of vectored execution and how to perform recursive calls,
a little bit more involved than allowed with MYSELF or RECURSE.

I know that the code, which you will find below, can be compacted.  But
idea was to make it run fast for an expense of some repetitve coding.

The code is written in TI-Forth which is an extension of fig-Forth. If
you will find somethng strange there - this is probably a graphics
support word, so do not pay big attention.  SRC is ShiftRightCyclicaly
by a number of bits taken from stack.  Please pay attentin that on
79-Standard and 83-Standard systems EXECUTE prefers compilation address
over 'CFA'. 

If you have a DOT - turns on pixel on screen in a given location,
what else - convert and have fun.
----------------------
    ...[Here} a  method  of  choice  is a vectored execution -
consult  "Starting Forth " by Leo Brodie - and defered execution
words,  as  described,  more  or  less, by Henry Laxen in "Forth
Dimensions",  Vol. III, no. 6. Here is a TI-Forth version. DEFER
is defining word introduced as follows:


  : DEFER    <BUILDS ' NOP CFA , DOES> @ EXECUTE ;

You  may  use  it to define DEFER HELLO and HELLO, when executed
will  just  do  nothing,  or  more precisely will execute a word
which  CFA  is stored under address returned by ' HELLO - namely
NOP.   A convenient way to change a behavior of HELLO is to us a
word IS.

  : IS CFA [COMPILE] ' ! ;

Now  define  :  HI  ." How are you, pal!" CR ;, : BYE ." See you
later!" CR ; and you may try to type

  ' HI   IS HELLO    HELLO
  ' BYE  IS HELLO    HELLO

    One thing to remember.  Due to a way in which ' operates IS
will give you expected results only when in interpretation mode.
If  you  wold  like  to  change  an  operation of HELLO inside a
defintion,  just  stick  a  proper CFA into PFA of HELLO.  It ia
possible to rewrite IS to make it "state-smart" but probably not
worth to bother.

    Back  to  recursion. You simply define DEFERed stubs to use
inside  recursive words.  After definitions are complete you are
using  IS  to redefine stubs into what you just defined.  Voila!
Instant  self-reference  of  any  degree  of  complication.  See
screens for an example of usage.


----------------------------------------------------------------

( Dragon curve * 1st scrn * Michal Jaegermann  1MAY86 )
BASE @ HEX  
40 VARIABLE STEP  \ SEGx draws segment and updates position
: SEG0   STEP @ 0 DO OVER OVER DOT    >R 1+ R> LOOP ;
: SEG1   STEP @ 0 DO OVER OVER DOT 1- >R 1+ R> LOOP ;
: SEG2   STEP @ 0 DO OVER OVER DOT 1-          LOOP ;
: SEG3   STEP @ 0 DO OVER OVER DOT 1- >R 1- R> LOOP ;
: SEG4   STEP @ 0 DO OVER OVER DOT    >R 1- R> LOOP ;
: SEG5   STEP @ 0 DO OVER OVER DOT 1+ >R 1- R> LOOP ;
: SEG6   STEP @ 0 DO OVER OVER DOT 1+          LOOP ;
: SEG7   STEP @ 0 DO OVER OVER DOT 1+ >R 1+ R> LOOP ;

' SEG0 CFA
   VARIABLE EVENHD   ' SEG2 CFA , ' SEG4 CFA , ' SEG6 CFA ,
' SEG1 CFA
   VARIABLE  ODDHD   ' SEG3 CFA , ' SEG5 CFA , ' SEG7 CFA , -->

( Dragon curve * 2nd scrn * Michal Jaegermann  1MAY86 )
0 VARIABLE SEGTBLE
0 VARIABLE HEADING  0 VARIABLE XCOR  0 VARIABLE YCOR
: SEGMENT ( x y -- x1 y1 ) \ vectored through SEGTBLE
    HEADING @ 06 AND SEGTBLE @ + @ EXECUTE  ;

DEFER <LDRAGON>   DEFER <RDRAGON>

: LDRAGON ( n -- n-1 or draw )
    -DUP IF 1- DUP <LDRAGON>  2 HEADING +! <RDRAGON>
         ELSE XCOR @ YCOR @ SEGMENT YCOR ! XCOR ! ENDIF ;
: RDRAGON ( n -- n-1 or draw )
    -DUP IF 1- DUP <LDRAGON> -2 HEADING +! <RDRAGON>
         ELSE XCOR @ YCOR @ SEGMENT YCOR ! XCOR ! ENDIF ;

' LDRAGON IS <LDRAGON>   ' RDRAGON IS <RDRAGON>          -->

( Dragon curve * 3rd scrn * Michal Jaegermann  1MAY86 )

: SETUP ( level -- level) 0 MAX 0E MIN
  DUP 1 AND IF ODDHD ELSE EVENHD ENDIF SEGTBLE !
  DUP MINUS HEADING ! 80 OVER 1+ 2 / SRC STEP !
  48 XCOR   ! 50 YCOR !
  F0 DCOLOR ! GRAPHICS2  07 7 VWTR  DRAW ;

: DRAGON ( level -- )  SETUP LDRAGON ;

: DRAGONS 0F 0 DO I DRAGON KEY  \ 'break' has a scan code 02
       2 = IF LEAVE ENDIF LOOP TEXT ;

BASE !

----------------------------------------------------------------
Michal Jaegermann
Myrias Research Corporation
Edmonton. Alberta, CANADA
...ihnp4!alberta!myrias!mj