For Loops

A for Loop is a loop that iterates over a predefined set of numbers, or a pre-defined set of values in a Sequence, and executes a statement or a block of statements once for each value. An iterator variable is defined and maintained by the loop, allowing each iteration access to the value that it is asked to operate on.

There are two basic types of For Loops: for/to loops that iterate over a range of numbers, and for/in loops (also referred to as for each loops) that iterate over items in a Sequence.

for/to Loops

A simple for/to loop uses the following syntax:

for i: Integer := 0 to 10 do {...}

The i: Integer defines the name and data type of the identifier. The For loop always introduces its own variable for the loop, even when a variable of the same name is already defined in the outside scope. This is different from legacy Pascal dialects, which uncleanly allow the reuse of a variable (often with undefined results) before and after the for loop itself.

Steps

By default, a for/to loop iterates over each value from the start to the end in increments of 1 (one). Optionally, the steps keyword can be used alongside a different increment. If specified, the loop will iterate in larger steps, in the example below only running the loop for every other number.

for i: Integer := 0 to 10 step 2 do {...}

If the step size does not cause the loop counter to exactly reach the end value of the loop, the loop will end with the last iteration that is smaller than the end value. For example, the code below will iterate across 0, 3, 9, and then stop. It will hit neither 10 nor 12.

for i: Integer := 0 to 10 step 3 do {...}

The range can be specified as a constant or as a dynamic expression, but (unlike in C# and many other languages) the end value will only be evaluated once, at the beginning of the loop. Changes to the step count or the loop range from inside the loop will have no effect on the duration of the loop.

Backwards Loops

A for loop can also be made to count downwards instead of upwards, by replacing the to keyword with downto, as shown here:

for i: Integer := 10 downto 0 do {...}

Note that it is up to the developer to ensure that the start and end value have the proper relationship to each other (i.e. start being smaller than end for a to loop, and higher than end for a downto loop), otherwise the loop may run through the full range of the Integer type and will "wrap around" when it reaches the type's minimum or maximum range.

for/in Loops

for/in (or for each) loops are a second variation of for loops. Rather than iterating over a range of numbers, they iterate over all elements of a Sequence or sequence-compatible type (such as an Array).

A simple for/in loop uses the following syntax:

for each i in list do {...}

where list can be any sequence of values.

By default, the type for the iterator variable i is usually inferred from the type of sequence, but it can also be specified manually, using the expected syntax:

for each i: String in list do {...}

When specified, the compiler will enforce that the declared type matches the type of the sequence and emit an error if it does not match (for example if, in the example above, list was a Integer, not assignment compatible with String).

For legacy reasons, the each keyword is optional and can be omitted, although we encourage to use it.

Matching

As a variation on this, the optional matching keyword can be used, along with an explicitly specified type name, to limit the for loop to only run for those items of a sequence that match in type. This is helpful if you have a sequence of a certain base type, but only want to iterate over the items of a specific descendant type. For example:

var list: sequence of Control;
for each matching b: Button in list do {...}

Here, list is a sequence that could contain any sort of Control (a made-up class) type. But the loop will only execute for those controls that actually are of type Button.

Indexes

Sometimes it is useful to keep count of the iterations of the loop in a numerical way, even in for/in loops. For example, when rendering a list of items, one might want to use different colors for even vs. odd items.

While it is of course possible to manually define and increment a counter variable, Oxygene provides an enhancement to the for loop syntax to take care of this:

for each s in list index i do {...}

In this example, s remains the loop iterator, and will contain the values obtained from the sequence as the loop progresses. At the same time, i is introduced as a second loop value of type Integer and will be incremented with each iteration.

for each from Shortcut Syntax

Oxygene provides shortcut syntax for combining a for/in loop and a from query expression.

The normal syntax for using an expression inside a for loop would look like this:

for each i in (from i2 in myList where i2 > 5) do {...}

Note how an extra variable needs to be defined inside the clause that, in essence, represents the same element as the outer loop variable.

You can write the same in a more natural way, by combining the iterator variable and the query expression variable into one:

for each from i in myList where i > 5 do {...}

Prematurely Exiting the Loop or a Loop Iteration

Like all loops, for Loops can be exited prematurely using the break and exit statements, and a single loop iteration can be cut short by using the continue statement, which jumps to the next loop iteration.

Parallel Loops

It's possible to process the body of the loop in parallel, usually leveraging multiple threads and CPU cores.

.NET Only

Parallel for loops are currently only supported on .NET.

A for loop can be turned parallel simply by adding the keyword to it, as shown below:

for parallel i: Integer := 0 to 10 do {...}

Using this syntax, the individual iterations of the loop are automatically spread over multiple threads and CPU cores. But this is done smartly, and in a way that leverages core OS resources to distribute the load onto a number of threads that makes sense for the current hardware. A loop of 1000 items will not just create a thousand threads, which would be terrible for performance. Instead, the number of threads and how to create them will be handled at runtime by the OS, and take into account factors such as the number of available CPU cores and overall load on the system at the time.

This is true for all Parallelism features in Oxygene (and Elements in general).

Although the loop will execute individual iterations asynchronously (and, nota bene, not necessarily in a predetermined order), the loop itself does not finish and pass execution to the code that follows it until all iterations are complete.

If an exception occurs in any one of the iterations, the loop is canceled, finishing the currently running iterations. The exception(s) will be wrapped in a new exception that will be re-thrown in the context of the original code and thread.

The use of exit is not allowed in parallel for loops. The break keyword can be used, and will stop the loop from starting up further iterations, but (similar to the exception behavior described above), any iterations already running will continue until they completed. The continue keyword will work as expected, as it only affects the current iteration.

Limitations of Parallel Loops

Parallel For Loops support both for/to and for each loop types. However, the downto and step syntaxes are currently not supported.

for Loops and begin/end blocks.

On its own, the for loop only takes a single statement to be executed for each iteration. To execute more than one statement, multiple statements can be grouped using a begin/end Block Statement:

for i: Integer := 0 to 10 do begin
  DoSomething();
  DoSomethingElse();
end;

See also