case
¶Gforth provides an extended case
that solves the problems of
the multi-exit loops discussed above, and offers additional options.
You can find a portable implementation of this extended case
in
compat/caseext.fs.
There are three additional words in the extension. The first is
?of
which allows general tests (rather than just testing for
equality) in a case
; e.g.,
: sgn ( n -- -1|0|1 ) ( n ) case dup 0 < ?of drop -1 endof dup 0 > ?of drop 1 endof \ otherwise leave the 0 on the stack 0 endcase ;
Note that endcase
drops a value, which works fine much of the
time with of
, but usually not with ?of
, so we leave a 0
on the stack for endcase
to drop. The n that is passed into
sgn
is also 0 if neither ?of
triggers, and that is then
passed out.
The second additional word is next-case
, which allows turning
case
into a loop. Our triple-exit loop becomes:
case condition1 ?of exit-code1 endof condition2 ?of exit-code2 endof condition3 ?of exit-code3 endof ... next-case common code afterwards
As you can see, this solves both problems of the variants discussed
above (see General loops with multiple exits). Note that
next-case
does not drop a value, unlike
endcase
.16
The last additional word is contof
, which is used instead of
endof
and starts the next iteration instead of leaving the
loop. This can be used in ways similar to Dijkstra’s guarded command
do, e.g.:
: gcd ( n1 n2 -- n ) case 2dup > ?of tuck - contof 2dup < ?of over - contof endcase ;
Here the two ?of
s have different ways of continuing the loop;
when neither ?of
triggers, the two numbers are equal and are
the gcd. Endcase
drops one of them, leaving the other as n.
You can also combine these words. Here’s an example that uses each of
the case
words once, except endcase
:
: collatz ( u -- ) \ print the 3n+1 sequence starting at u until we reach 1 case dup . 1 of endof dup 1 and ?of 3 * 1+ contof 2/ next-case ;
This example keeps the current value of the sequence on the stack. If
it is 1, the of
triggers, drops the value, and leaves the
case
structure. For odd numbers, the ?of
triggers,
computes 3n+1, and starts the next iteration with contof
.
Otherwise, if the number is even, it is divided by 2, and the loop is
restarted with next-case
.
The case
words are:
case
( compilation – case-sys ; run-time – ) core-ext “case”
Start a case
structure.
endcase
( compilation case-sys – ; run-time x – ) core-ext “end-case”
Finish the case
structure; drop x, and continue behind
the endcase
. Dropping x is useful in the original
case
construct (with only of
s), but you may have
to supply an x in other cases (especially when using
?of
).
next-case
( compilation case-sys – ; run-time – ) gforth-1.0 “next-case”
Restart the case
loop by jumping to the matching
case
. Note that next-case
does not drop a cell,
unlike endcase
.
of
( compilation – of-sys ; run-time x1 x2 – |x1 ) core-ext “of”
If x1=x2, continue (dropping both); otherwise, leave x1 on the
stack and jump behind endof
or contof
.
?of
( compilation – of-sys ; run-time f – ) gforth-1.0 “question-of”
If f is true, continue; otherwise, jump behind endof
or
contof
.
endof
( compilation case-sys1 of-sys – case-sys2 ; run-time – ) core-ext “end-of”
Exit the enclosing case
structure by jumping behind
endcase
/next-case
.
contof
( compilation case-sys1 of-sys – case-sys2 ; run-time – ) gforth-1.0 “cont-of”
Restart the case
loop by jumping to the enclosing
case
.
Internally, of-sys is an orig
; and case-sys is a cell
and some stack-depth information, 0 or more orig
s, and a
dest
.
The name next-case
has a -
,
unlike the other case
words, because VFX Forth has a
next-case
that works like Gforth’s next-case
, but also
contains a nextcase
that drops a value; in VFX you need to pair
next-case
with begincase
, however.