第四:具有多个出口点的便携式无限循环

Forth: Portable indefinite loop with multiple exit points

我需要实现一个具有多个出口点的无限循环。 不幸的是,最明显的解决方案 - REPEAT - UNTIL with multiple WHILE 在 Gforth 和 swapforth 中都不起作用(当然,下面示例中的循环可以使用 DO - LOOP 实现。但是,该代码只是一个演示。真正的问题与嵌入式系统中的硬件控制有关,所以循环确实是不确定的):

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

3 test1 

在"Thinking Forth"中引用了摩尔的声明:

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.

不幸的是我没有找到院长的解决方案。 有什么可移植的方法可以在 Forth 的无限循环中实现多个出口点吗?

经过一些实验,我创建了一个基于 DO +LOOP 的解决方案。 不知道和Dean Sanderson提出的是不是一样

我已经在Gforth和swapforth中测试成功了。 似乎可以创建任意数量的退出点。 无限循环是通过以下方式创建的:0 1 DO loop content here 0 +LOOP。 退出点由放置在 IF THEN 中的 LEAVE 创建。

示例代码:

: 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
;

测试结果:

> 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当然为一个定义提供了多个出口。您可以通过使用单独的词或更巧妙地使用引号来使循环体与定义相同:

: 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 ) ;

根据 ruvim 给出的评论,我测试了一个基于多个 WHILE 的解决方案,并附加了 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
;

确实有效。下面的测试显示了哪些代码部分因不同原因而退出循环。

循环在第一个 while 后退出:

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

循环在第 2 个 while 后退出:

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

循环在第 3 个 while 后退出:

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

因此,如果有人想将动作分配给每个 WHILE,则应按如下方式进行:

: 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
;

有趣的是以上内容如何扩展更多的 WHILE。 适用于 4 个 WHILE 的语法如下所示:

: 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
;

以上代码在Gforth 和swapforth 中都进行了测试。 有趣的问题是 return 不同可能解决方案中的堆栈占用。 (例如,在 J1B CPU 中,return 堆栈深度仅为 32 层)。