### 3.4 IN Loops

#### Testing for Sets with Zero Elements

Where a loop lies naturally over the elements of a set, e.g. OBJECT, the loop should be preceded by a test of whether the set has zero elements: this ensures that the predicate is proved even if the loop is never executed:

`IF     ?SIZE = SIZEOF OBJECTAND    (   ?SIZE EQ 0           OR  ?OBJECT IN OBJECT                   AND < loop code >       )`

If we simply looped ?OBJECT IN OBJECT and OBJECT had zero elements, the statement would resolve UNKNOWN. The rule would then end UNKNOWN (unless there were an OR clause) and this could cause backtracking in the calling rule.

#### What Happens with Duplicate or Blank Entries

One concern which people often have about looping over sets is what happens when there are duplicate entries or entries are blank. These cause no difficulties. The only problem is in getting the index number of the current position in the set, which can be done thus:

`IF     ?OBJECT IN OBJECTAND        ?I = INDEXOF OBJECT(@)`

The line ?I = INDEXOF OBJECT(@) obtains the current value of the loop counter (see section 2.b). It is so useful that it will often appear immediately after the start of an IN loop. Indeed, if it is to work, it must appear immediately after the start of the IN loop. For if it appears elsewhere, in particular after a predicate, it is possible that the position of the internal index in OBJECT may have been changed. WHILE loops can often be replaced directly by an IN loop followed by this statement.

#### Nested Loops over the Same Set

Note that as the loop counter is maintained separately for each loop (and is distinct from the pointer used by the SETIND command) it is possible to loop over a set twice in a nest, or even recursively:

`IF     ?OBJECT IN OBJECTAND        ?I = INDEXOF OBJECT(@)      AND        ?OBJECT2 IN OBJECTAND            ?J = INDEXOF OBJECT(@)`

#### Looping over Some Members of a Set

If a loop lies over some (but not all) members of a set it is still worth writing it as an IN loop over the entire set: this avoids any problems associated with the upper limit on a WHILE loop. Suppose that we want to ignore the first 100 elements of OBJECT. We can do this as follows:

`IF     ?SIZE = SIZEOF OBJECTAND    (   ?SIZE LT 100           OR  ?OBJECT IN OBJECT                   AND ?I = INDEXOF OBJECT(@)                   AND ?I GE 100                                      AND < loop code >       )`

This loops over ?OBJECT in OBJECT and then gets the internal index ?I of ?OBJECT. The test `?I GE 100` fails if we are looking at one of the first 100 elements of OBJECT, whereupon the interpreter backtracks to the loop over ?OBJECT and tries the next element. Once we are past the first 100 elements, the test `?I GE 100` succeeds and the rest of the loop code is executed.