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 := 0 to 10 do
DoSomething;
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.
The type for the loop variable will be inferred from then start and end value, but can also optionally be specificed explcitly:
for i: Integer := 0 to 10 do
DoSomething;
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 := 0 to 10 step 2 do
DoSomething();
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 := 0 to 10 step 3 do
DoSomething();
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 := 10 downto 0 do
DoSomething();
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
DoSomething();
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 just as with for
/to
loops, 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
DoSomething();
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
DoSomething(s);
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.
Omitting the Loop Variable
Sometimes it is useful to just lopp over a collection without need to access the actual elements. In this case, a nil
Dicardable can be used for the loop variable, or it can be omitted altogether:
for each nil in list do
DoSomething();
or simply:
for each in list do
DoSomething();
This can avoid "variable is ot used" warnings.
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
DoSomething();
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
DoSomething();
Prematurely Exiting the Loop or a Loop Iteration
Like all loops, for
Loops can be exited prematurely using the break
and exit
statements and raise
, 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.
A for
loop can be turned parallel simply by adding the keyword to it, as shown below:
for parallel i := 0 to 10 do
DoSomething();
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
- Loop Statements
- Flow Control Statements
begin
/end
Block Statementswhile
/do
andrepeat
/until
loopsloop
loops, also referred to as Infinite Loops- Parallelism
- Sequence