#### 6.9.3 Counted Loops ¶

The basic counted loop is:

```limit start ?DO
body
LOOP
```

This performs one iteration for every integer, starting from start and up to, but excluding limit. The counter, or index, can be accessed with `i`. For example, the loop:

```10 0 ?DO
i .
LOOP
```

prints `0 1 2 3 4 5 6 7 8 9`

The index of the innermost loop can be accessed with `i`, the index of the next loop with `j`, and the index of the third loop with `k`.

You can access the limit of the innermost loop with `i'` and `i'`-`i` with `delta-i`. E.g., running

```: foo 7 5 ?do cr i . i' . delta-i . loop ;
```

prints

```5 7 2
6 7 1
```

The loop control data are kept on the return stack, so there are some restrictions on mixing return stack accesses and counted loop words. In particuler, if you put values on the return stack outside the loop, you cannot read them inside the loop12. If you put values on the return stack within a loop, you have to remove them before the end of the loop and before accessing the index of the loop.

There are several variations on the counted loop:

• `LEAVE` leaves the innermost counted loop immediately; execution continues after the associated `LOOP` or `NEXT`. For example:
```10 0 ?DO  i DUP . 3 = IF LEAVE THEN LOOP
```

prints `0 1 2 3`

• `UNLOOP` prepares for an abnormal loop exit, e.g., via `EXIT`. `UNLOOP` removes the loop control parameters from the return stack so `EXIT` can get to its return address. For example:
```: demo 10 0 ?DO i DUP . 3 = IF UNLOOP EXIT THEN LOOP ." Done" ;
```

prints `0 1 2 3`

• If start is greater than limit, a `?DO` loop is entered (and `LOOP` iterates until they become equal by wrap-around arithmetic). This behaviour is usually not what you want. Therefore, Gforth offers `+DO` and `U+DO` (as replacements for `?DO`), which do not enter the loop if start is greater than limit; `+DO` is for signed loop parameters, `U+DO` for unsigned loop parameters.
• `?DO` can be replaced by `DO`. `DO` always enters the loop, independent of the loop parameters. Do not use `DO`, even if you know that the loop is entered in any case. Such knowledge tends to become invalid during maintenance of a program, and then the `DO` will make trouble.
• `LOOP` can be replaced with `n +LOOP`; this updates the index by n instead of by 1. The loop is terminated when the border between limit-1 and limit is crossed. E.g.:
```4 0 +DO  i .  2 +LOOP
```

prints `0 2`

```4 1 +DO  i .  2 +LOOP
```

prints `1 3`

• The behaviour of `n +LOOP` is peculiar when n is negative:
```-1 0 ?DO  i .  -1 +LOOP
```

prints `0 -1`

```0 0 ?DO  i .  -1 +LOOP
```

prints nothing.

We recommend not combining `?DO` with `+LOOP`. Gforth offers several alternatives:

If you want `-1 +LOOP`’s behaviour of including an iteration where `I`=limit, start the loop with `-[DO` or `U-[DO` (where the `[` is inspired by the mathematical notation for inclusive ranges, e.g., [1,n]):

```-1 0 -[DO  i .  -1 +LOOP
```

prints `0 -1`.

```0 0 -[DO  i .  -1 +LOOP
```

prints `0`.

```0 -1 -[DO  i .  -1 +LOOP
```

prints nothing.

If you want to exclude the limit, you instead use `1 -LOOP` (or generally `u -LOOP`) and start the loop with `?DO`, `-DO` or `U-DO`. `-LOOP` terminates the loop when the border between limit+1 and limit is crossed. E.g.:

```-2 0 -DO  i .  1 -LOOP
```

prints `0 -1`

```-1 0 -DO  i .  1 -LOOP
```

prints `0`

```0 0 -DO  i .  1 -LOOP
```

prints nothing.

Unfortunately, `+DO`, `U+DO`, `-DO`, `U-DO` and `-LOOP` are not defined in Standard Forth. However, an implementation for these words that uses only standard words is provided in compat/loops.fs.

• A common task is to iterate over the elements of an array, forwards or backwards. Iterating over the addresses of the elements has two benefits: It avoids the need to keep the start address of the array around, reducing the data stack load; and it avoids the need to perform address computations in every iteration. The disadvantage is that, starting with the usual array representations addr uelems or addr ubytes, some processing is required to produce a start and limit address. Gforth has `bounds` for getting there from the addr ubytes representation, so you can write a forward loop through a cell array `v` as:
```create v 1 , 3 , 7 ,
: foo v 3 cells bounds U+DO i  . cell +LOOP ;
foo
```

which prints `1 3 7`. Preprocessing the inputs for walking backwards is more involved, so Gforth provide a loop construct of the form `MEM-DO`...`LOOP` that does it for you: It takes an array in addr ubytes representation and the element size, and iterates over the addresses of the elements in backwards order:

```create v 1 , 3 , 7 ,
: foo1 v 3 cell array>mem MEM-DO i  . LOOP ;
foo1
```

This prints `7 3 1`. `ARRAY>MEM` converts the addr uelems uelemsize representation into the addr ubytes uelemsize representation expected by `MEM-DO`. This loop is finished with `LOOP` which decrements by uelemsize when it finishes a `MEM-DO`.

Gforth also adds `MEM+DO` for completeness. It takes the same parameters as `MEM-DO`, but walks forwards through the array:

```create v 1 , 3 , 7 ,
: foo2 v 3 cell array>mem MEM+DO i  . LOOP ;
foo2
```

prints `1 3 7`.

• Another counted loop is:
```n
FOR
body
NEXT
```

This is the preferred loop of native code compiler writers who are too lazy to optimize `?DO` loops properly. This loop structure is not defined in Standard Forth. In Gforth, this loop iterates n+1 times; `i` produces values starting with n and ending with 0. Other Forth systems may behave differently, even if they support `FOR` loops. To avoid problems, don’t use `FOR` loops.

The counted-loop words are:

````?DO` ( compilation – do-sys ; run-time w1 w2 – | loop-sys  ) core-ext “question-do”
```

See Counted Loops.

````+DO` ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-0.2 “plus-do”
```

See Counted Loops.

````U+DO` ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-0.2 “u-plus-do”
```

See Counted Loops.

````bounds` ( u1 u2 – u3 u1 ) gforth-0.2 “bounds”
```

Given a memory block represented by starting address addr and length u in aus, produce the end address addr+u and the start address in the right order for `u+do` or `?do`.

````-[do` ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-experimental “minus-bracket-do”
```

Start of a counted loop with negative stride; Skips the loop if n2<n1; such a counted loop ends with `+loop` where the increment is negative; it runs as long as `I`>=n1.

````u-[do` ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-experimental “u-minus-bracket-do”
```

Start of a counted loop with negative stride; Skips the loop if u2<u1; such a counted loop ends with `+loop` where the increment is negative; it runs as long as `I`>=u1.

````-DO` ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-0.2 “minus-do”
```

See Counted Loops.

````U-DO` ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-0.2 “u-minus-do”
```

See Counted Loops.

doc-array>mem

````mem+do` ( compilation – w xt do-sys; run-time addr ubytes +nstride –  ) gforth-experimental “mem-plus-do”
```

Starts a counted loop that starts with `I` as addr and then steps upwards through memory with nstride wide steps as long as `I`<addr+ubytes. Must be finished with loop.

````mem-do` ( compilation – w xt do-sys; run-time addr ubytes +nstride –  ) gforth-experimental “mem-minus-do”
```

Starts a counted loop that starts with `I` as addr+ubytes-ustride and then steps backwards through memory with -nstride wide steps as long as `I`>=addr. Must be finished with loop.

````DO` ( compilation – do-sys ; run-time w1 w2 – loop-sys  ) core “DO”
```

See Counted Loops.

````FOR` ( compilation – do-sys ; run-time u – loop-sys  ) gforth-0.2 “FOR”
```

See Counted Loops.

````LOOP` ( compilation do-sys – ; run-time loop-sys1 – | loop-sys2  ) core “LOOP”
```

See Counted Loops.

````+LOOP` ( compilation do-sys – ; run-time loop-sys1 n – | loop-sys2  ) core “plus-loop”
```

See Counted Loops.

````-LOOP` ( compilation do-sys – ; run-time loop-sys1 u – | loop-sys2  ) gforth-0.2 “minus-loop”
```

See Counted Loops.

````NEXT` ( compilation do-sys – ; run-time loop-sys1 – | loop-sys2  ) gforth-0.2 “NEXT”
```

See Counted Loops.

````i` ( R:n – R:n n ) core “i”
```

n is the index of the innermost counted loop.

````j` ( R:n R:w1 R:w2 – n R:n R:w1 R:w2 ) core “j”
```

n is the index of the next-to-innermost counted loop.

````k` ( R:n R:w1 R:w2 R:w3 R:w4 – n R:n R:w1 R:w2 R:w3 R:w4 ) gforth-0.3 “k”
```

n is the index of the third-innermost counted loop.

````i'` ( R:w R:w2 – R:w R:w2 w ) gforth-0.2 “i-tick”
```

The limit of the innermost counted loop

````delta-i` ( r:ulimit r:u – r:ulimit r:u u2 ) gforth-1.0 “delta-i”
```

u2=`I'`-`I` (difference between limit and index).

````LEAVE` ( compilation – ; run-time loop-sys –  ) core “LEAVE”
```

See Counted Loops.

````?LEAVE` ( compilation – ; run-time f | f loop-sys –  ) gforth-0.2 “question-leave”
```

See Counted Loops.

````unloop` ( R:w1 R:w2 – ) core “unloop”
```
````DONE` ( compilation do-sys – ; run-time –  ) gforth-0.2 “DONE”
```

resolves all LEAVEs up to the do-sys

The standard does not allow using `CS-PICK` and `CS-ROLL` on do-sys. Gforth allows it, except for the do-sys produced by `MEM+DO` and `MEM-DO`, but it’s your job to ensure that for every `?DO` etc. there is exactly one `UNLOOP` on any path through the definition (`LOOP` etc. compile an `UNLOOP` on the fall-through path). Also, you have to ensure that all `LEAVE`s are resolved (by using one of the loop-ending words or `DONE`).

#### Footnotes

##### (12)

well, not in a way that is portable.