Skip to content
Cameron Purdy edited this page Apr 4, 2020 · 4 revisions

The for statement is generally used to loop over a range of values or the contents of a data structure, executing a block of code for each loop iteration. It is, in many ways, a construct of convenience, intended to eliminate a few lines of code that would be necessary to accomplish the same result with a while or do statement. As such, its purpose is to assist both in the writeability and readability for a number of common patterns in looping code, while helping to reduce errors.

There are two forms of the ForStatement, based on two different forms of the ForCondition. The first is the traditional C-style for condition that has a separate initialization, test, and modification section. The second is the "for-each" style that specifies a range of values or a value of a container type whose values are to be iterated. These two forms will be explored separately, below.

    ForStatement:
        for ( ForCondition ) StatementBlock
    
    ForCondition:
        VariableInitializationListopt ; ConditionListopt ; VariableModificationListopt
        OptionalDeclaration : Expression
        ( OptionalDeclarationList , OptionalDeclaration ) : Expression
    
    VariableInitializationList:
        VariableInitializer
        VariableInitializationList , VariableInitializer
    
    VariableInitializer:
        OptionalDeclaration = Expression
        ( OptionalDeclarationList , OptionalDeclaration ) = Expression
    
    VariableModificationList:
        VariableModification
        VariableModificationList , VariableModification
    
    VariableModification:
        Assignment
        Expression

From IfStatement:

    ConditionList:
        Condition
        ConditionList , Condition

    Condition:
        Expression
        OptionalDeclaration ConditionalAssignmentOp Expression
        ( OptionalDeclarationList , OptionalDeclaration ) ConditionalAssignmentOp Expression

    ConditionalAssignmentOp:
        :=
        ?=

And from VariableStatement:

    OptionalDeclarationList:
        OptionalDeclaration
        OptionalDeclarationList , OptionalDeclaration

    OptionalDeclaration:
        Assignable
        VariableTypeExpression Name

    VariableTypeExpression:
        val
        var
        TypeExpression

    Assignable:
        Name
        TernaryExpression . Name
        TernaryExpression ArrayIndexes

The C-style ForStatement

The C-style for statement uses a three-part ForCondition, any part of which can be left blank:

  • An initialization portion, which is performed when the statement is entered;
  • A test portion, which is performed at the beginning of each iteration;
  • A modification portion, which is performed at the end of each iteration.

The initialization portion is used to configure the initial state of the loop. Any number of declarations and assignments can be included in this portion; for example:

x=0, Int i=3, String s="hello", nums[0]=0, Boolean f=False

The test portion is a ConditionList that yields a Boolean value, similar to that used in the IfStatement; for example:

x<100 && s=="hello"

Lastly, the modification portion allows a number of actions to be performed at the end of each loop iteration:

++x, foo(), i *= 2, f = !f

The above examples are atrocious in terms of readability; they are intended solely to illustrate the richness of the capabilities of the for statement. For readability, the initialization portion should only be used to declare and initialize (i) loop control variables and (ii) variables local to the loop whose state is carried over from one iteration to the next. Similarly, the modification portion should be limited, as much as possible, to modifying only loop control variables.

In general terms, the C-style of for statement can be re-written as a while statement; consider:

for (A; B; C)
    {
    D;
    }

The above for statement could be re-written as a while statement inside of a statement block:

{
A;
while (B)
    {
    D;
    C;
    }
}

Combined with the possibility of a continue statement within the loop body, it quickly becomes obvious why the C-style for statement exists; the alternative approaches are simply too ugly and unwieldy.

Execution of the for statement begins with the execution of the VariableInitializationList, if any. Each comma-separated item in the initialization section is executed left-to-right and permitted to short-circuit; when a short-circuit occurs, execution continues with the next item. Then the test section, if specified, is evaluated: If it yields the Boolean value False, or it short-circuits, then the for statement completes; otherwise, if it yields the Boolean value True, or if no test portion is specified, then the statement block body of the for statement is executed. Then the execution proceeds to the VariableModificationList, if any; like the initialization section, each comma-separated item in the modification section is evaluated left-to-right and permitted to short-circuit, and a short-circuit causes execution to continue with the next item. Finally, the for statement loops back to the test section.

Execution of a break statement that applies to the for statement causes the for statement to complete.

Execution of a continue statement that applies to the for statement causes the for statement to advance its execution to the VariableModificationList.

Like the while statement, the for statement labeled by a LabeledStatement provides two read-only label variables that expose the state of the loop:

Name Type Description
first Boolean True iff this is the first iteration of the loop
count Int The count of completed iterations of the loop

The VariableInitializationList of VariableInitializers is reachable if the for statement is reachable. The first VariableInitializer is reachable if the VariableInitializationList is reachable. If a VariableInitializer short-circuits, then the VariableInitializer completes. Each subsequent VariableInitializer is reachable if the VariableInitializer preceding it completes. The VariableInitializationList completes if it is reachable and its last VariableInitializer completes; if there is no VariableInitializationList (i.e. if the list is empty), then the VariableInitializationList is considered to complete if it is reachable.

The test expression is reachable if the VariableInitializationList completes. If the test expression short-circuits, then the test expression completes as if it evaluated to the value False. If the test expression is absent, then the test expression completes as if it were the constant value True. The statement block body of the for statement is reachable if the test expression completes and is not the constant value False.

The VariableModificationList is reachable if the body completes or if a continue statement that applies to the for statement is reachable. The first VariableModification is reachable if the VariableModificationList is reachable. If a VariableModification short-circuits, then the VariableModification completes. Each subsequent VariableModification is reachable if the VariableModification preceding it completes. The VariableModificationList completes if it is reachable and its last VariableModification completes; if there is no VariableModificationList (i.e. if the list is empty), then the VariableModificationList is considered to complete if it is reachable.

The for statement completes if the test expression completes and is not the constant True, or if a break statement that applies to the for statement is reachable.

Definite assignment rules:

  • The VAS before the VariableInitializationList is the VAS before the for statement.
  • The VAS before the first VariableInitializer is the VAS before the VariableInitializationList.
  • The VAS before the each subsequent VariableInitializer is the VAS after the previous VariableInitializer.
  • The VAS after the VariableInitializationList is the VAS after the last VariableInitializer. (In the case of an empty VariableInitializationList, the VAS after the VariableInitializationList is the VAS before the VariableInitializationList.)
  • The VAS before the test expression is the VAS after the VariableInitializationList.
  • If the test expression can short-circuit, then the VAS at each possible point of short-circuiting is joined with the VASF after the test expression.
  • The VAS before the statement block is the VAST after the test expression.
  • The VAS before the VariableModificationList is the VAS after the statement block.
  • The VAS before the first VariableModification is the VAS before the VariableModificationList.
  • The VAS before the each subsequent VariableModification is the VAS after the previous VariableModification.
  • The VAS after the VariableModificationList is the VAS after the last VariableModification. (In the case of an empty VariableModificationList, the VAS after the VariableModificationList is the VAS before the VariableModificationList.)
  • The VAS after the for statement is the VASF after the test expression joined with the VAS from each reachable break statement that applies to the for statement.

The for statement provides a local variable scope for any declarations in the ForCondition; that scope exists to the end of the for statement, which is to say that the statement block is nested within that scope. The statement block naturally provides a local variable scope, because it is a statement block.

The “for-each”-style ForStatement

The “for-each” style of the for statement is used to loop over a range of values, or the contents of a container type, such as an Iterator, Sequence, List, Array, or Map. For example:

for (String name : ["Tom", "Chris", "Caroline"])
    {
    console.println(name);
    }

The supported types of the expression, in order of precedence, are:

  • Iterator – The for statement loops through the contents of the Iterator.
  • Range – The for statement loops through the contents of the Range by its Sequential ElementType (and not by using an Iterator).
  • Sequence – The for statement loops through the contents of the Sequence using an Int index (and not by using an Iterator).
  • Map – The for statement creates an Iterator over the keys Set (or the entries Set, if the Entry or the value thereof is required) of the Map, and then loops through the contents of the Iterator.
  • Iterable – The for statement requests an Iterator from the Iterable object, and then loops through the contents of the Iterator.

Additionally, if the value from any of the above is a Tuple, then the OptionalDeclaration permits assignment from the fields of the Tuple. Consider the following example, in which a list contains tuples of first and last names:

String format(List<Tuple<String, String>> names)
    {
    StringBuffer result = "";
    Append: for ((String first, String last) : names)
        {
        if (!Append.first)
            {
            result += ", ";
            }
        result += $"{first} {last}";
        }
    return result.toString();
    }

The for statement operating on an Iterator or an Iterable container and labeled by a LabeledStatement provides two read-only label variables that expose the state of the loop:

Name Type Description
first Boolean True iff this is the first iteration of the loop
count Int The count of completed iterations of the loop

The for statement operating on a Range or Sequence and labeled by a LabeledStatement provides three read-only label variables that expose the state of the loop:

Name Type Description
first Boolean True iff this is the first iteration of the loop
last Boolean True iff this is the last iteration of the loop
count Int The count of completed iterations of the loop

The for statement operating on a Map<Key,Value> and labeled by a LabeledStatement provides three read-only label variables that expose the state of the loop:

Name Type Description
first Boolean True iff this is the first iteration of the loop
entry Entry The current Map.Entry being iterated
count Int The count of completed iterations of the loop

The ForCondition is reachable if the for statement is reachable; the ForCondition completes if it is reachable and the expression completes or short-circuits. The statement block is reachable if the ForCondition expression completes (i.e. if the ForCondition can complete without the expression short-circuiting). The for statement completes if the ForCondition completes.

Definite assignment rules:

  • The VAS before the ForCondition is the VAS before the for statement.
  • The VAS before the statement block is the VAST after the ForCondition.
  • If the ForCondition can short-circuit, then the VAS at each possible point of short-circuiting is joined with the VASF after the ForCondition.
  • The VAS after the for statement is the VAS after the statement block joined with the VASF after the ForCondition.

The for statement provides a local variable scope for any declarations in the ForCondition; that scope exists to the end of the for statement, which is to say that the statement block is nested within that scope. The statement block naturally provides a local variable scope, because it is a statement block.