简体   繁体   中英

Forth: Portable indefinite loop with multiple exit points

I need to implement an idefinite loop with multiple exit points. Unfortunately the most obvious solution - REPEAT - UNTIL with multiple WHILE doesn't work neither in Gforth nor in swapforth (Of course the loop in the example below may be implemented with DO - LOOP. However, that code is just a demonstration. The real problem is related to hardware control in an embedded system, so the loop indeed must be indefinite) :

: test1 ( step -- step count )
    0
    begin
      over +
      dup .
      dup 20 < while
      dup 13 = while
    repeat
;

3 test1 

In "Thinking Forth" there is Moore's statement quoted:

Many times conditionals are used to get out of loops. That particular use can be avoided by having loops with multiple exit points. This is a live topic, because of the multiple WHILE construct which is in poly- Forth but hasn't percolated up to Forth '83. It's a simple way of defining multiple WHILEs in the same REPEAT. Also Dean Sanderson [of Forth, Inc.] has invented a new construct that introduces two exit points to a DO LOOP. Given that construction you'll have fewer tests.

Unfortunately I failed to find the Dean's solution. Is there any portable way to implement multiple exit points in the indefinite loop in Forth?

After some experiments, I have created a solution based on a DO +LOOP. I don't know if it is the same as the one proposed by Dean Sanderson.

I have successfully tested it in Gforth and in swapforth. It seems that it is possible to create the arbitrary number of exit points. The indefinite loop is created via: 0 1 DO loop content here 0 +LOOP. The exit points are created by LEAVE placed in IF THEN.

Example code:

: test1 ( start step -- count step )
    swap
    1 0 do
      over +
      dup .
      dup 20 > if 
        ." >20 "
        leave
      then
      dup 13 = if
        ." =13 "
        leave
      then
      dup 17 = if
        ." =17 "
        leave
      then
    0 +loop
;

Test results:

> 1 3 test1
 4 7 10 13 =13  ok
> 2 3 test1
 5 8 11 14 17 =17  ok
> 0 3 test1
 3 6 9 12 15 18 21 >20  ok

EXIT of course provides multiple exits to a definition. You can make the body of the loop one and the same with a definition either by having a separate word or, more neatly, by using quotations:

: test ( start step -- count step )
  swap [: begin
    over + dup .
    dup 20 > if ." >20" exit then
    dup 13 = if ." =13" exit then
    dup 17 = if ." =17" exit then
  again ;] execute
  ( EXITs from loop continue here ) ;

Basing on the comment given by ruvim, I have tested a solution based on multiple WHILE resolved with additional THEN:

: test1 ( step start -- step count )
    cr
    begin
      over +
      dup . cr
      dup 30 < while
      dup 13 <> while
      dup 17 <> while
    repeat
        ." before 1st else" cr
    else
        ." after 1st else" cr
    then
        ." before 2nd else" cr
    else 
        ." after 2nd else" cr
    then
;

Indeed it works. The tests below show what sections of code are executed for different reasons to leave the loop.

Loop exited after the 1st while:

5 1 test1 
6 
11 
16 
21 
26 
31 
after 2nd else
 ok

Loop exited after the 2nd while:

5 3 test1 
8 
13 
after 1st else
before 2nd else
 ok

Loop exited after the 3rd while:

5 2 test1 
7 
12 
17 
before 1st else
before 2nd else
 ok

So if somebody wants to assign the action to each WHILE, it should be done as follows:

: test1 ( step start -- step count )
    cr
    begin
      over +
      dup . cr
      dup 30 < while
      dup 13 <> while
      dup 17 <> while
    repeat
    ." exited after the 3rd while" cr
    else
    ." exited after the 2nd while" cr
    then
    else
    ." exited after the 1st while" cr
    then
;

The interesting thing would be how the above scales for even more WHILE. The syntax that works for 4 WHILEs is shown below:

: test1 ( step start -- step count )
    cr
    begin
      over +
      dup . cr
      dup 30 < while
      dup 13 <> while
      dup 17 <> while
      dup 19 <> while
    repeat
    ." exited after the 4th while" cr
    else
    ." exited after the 3nd while" cr
    then
    else
    ." exited after the 2nd while" cr
    then
    else
    ." exited after the 1st while" cr
    then
;

The above code was tested both in Gforth and in swapforth. The interesting question is the return stack occupancy in different possible solutions. (Eg in the J1B CPU the return stack depth is only 32 levels).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM