Difference between revisions of "For"

From Free Pascal wiki
Jump to navigationJump to search
m (Deleted categories included in page template)
m (Add link to Set page)
 
(11 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
{{for}}
 
{{for}}
  
<syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight> is a [[Keyword|keyword]] used in conjunction with other keywords to create loops.
+
<syntaxhighlight lang="pascal" inline>for</syntaxhighlight> is a [[Keyword|keyword]] used in conjunction with other keywords to create [[Loops|loops]].
  
 
== loop with iterator variable ==
 
== loop with iterator variable ==
<syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight> used along with <syntaxhighlight lang="pascal" enclose="none">to</syntaxhighlight>/<syntaxhighlight lang="pascal" enclose="none">downto</syntaxhighlight> and [[Do|<syntaxhighlight lang="pascal" enclose="none">do</syntaxhighlight>]] constructs a loop in which the value of a control variable is incremented or decremented by <syntaxhighlight lang="pascal" enclose="none">1</syntaxhighlight> passing every iteration.
+
<syntaxhighlight lang="pascal" inline>for</syntaxhighlight> used along with <syntaxhighlight lang="pascal" inline>to</syntaxhighlight>/<syntaxhighlight lang="pascal" inline>downto</syntaxhighlight> and [[Do|<syntaxhighlight lang="pascal" inline>do</syntaxhighlight>]] constructs a loop in which the value of a control variable is incremented or decremented by <syntaxhighlight lang="pascal" inline>1</syntaxhighlight> passing every iteration.
  
 
=== behavior ===
 
=== behavior ===
Line 13: Line 13:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
In this example <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> is first initialized with the value of <syntaxhighlight lang="pascal" enclose="none">start</syntaxhighlight> (but cmp. [[#legacy|§ “legacy”]] below).
+
In this example <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> is first initialized with the value of <syntaxhighlight lang="pascal" inline>start</syntaxhighlight> (but cmp. [[#legacy|§ “legacy”]] below).
If and as long as <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> is not greater than <syntaxhighlight lang="pascal" enclose="none">finalValue</syntaxhighlight>, the [[Begin|<syntaxhighlight lang="pascal" enclose="none">begin</syntaxhighlight>]] [[End|<syntaxhighlight lang="pascal" enclose="none">end</syntaxhighlight>]] block with all its statements is executed.
+
If and as long as <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> is not greater than <syntaxhighlight lang="pascal" inline>finalValue</syntaxhighlight>, the [[Begin|<syntaxhighlight lang="pascal" inline>begin</syntaxhighlight>]] … [[End|<syntaxhighlight lang="pascal" inline>end</syntaxhighlight>]] [[Frame|frame]] with all its [[statement]]s is executed.
By reaching <syntaxhighlight lang="pascal" enclose="none">end</syntaxhighlight> <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> is [[Inc|incremented]] by <syntaxhighlight lang="pascal" enclose="none">1</syntaxhighlight> and the comparison is made, whether another iteration, whether the statement-[[Block|block]] is executed again.
+
By reaching <syntaxhighlight lang="pascal" inline>end</syntaxhighlight> <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> is [[Inc|incremented]] by <syntaxhighlight lang="pascal" inline>1</syntaxhighlight> and the comparison is made, whether another iteration, whether the statement-frame is executed again.
  
 
=== reverse direction ===
 
=== reverse direction ===
By exchanging <syntaxhighlight lang="pascal" enclose="none">to</syntaxhighlight> with <syntaxhighlight lang="pascal" enclose="none">downto</syntaxhighlight>, the variable – in the example <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> – is [[Dec|decremented]] by <syntaxhighlight lang="pascal" enclose="none">1</syntaxhighlight> and the condition becomes “as long as the control variable is not less than the final value.”
+
By exchanging <syntaxhighlight lang="pascal" inline>to</syntaxhighlight> with <syntaxhighlight lang="pascal" inline>downto</syntaxhighlight>, the variable – in the example <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> – is [[Dec|decremented]] by <syntaxhighlight lang="pascal" inline>1</syntaxhighlight> and the condition becomes “as long as the control variable is not less than the final value.”
  
 
=== constraints ===
 
=== constraints ===
 
==== scope requirements ====
 
==== scope requirements ====
 
The control variable has to be [[Local variables|local]] inside nested [[Routine|routines]].
 
The control variable has to be [[Local variables|local]] inside nested [[Routine|routines]].
A routine is nested, if [[Procedural variable|routine variables]] tagged with the modifier <syntaxhighlight lang="delphi" enclose="none">is nested</syntaxhighlight> can store its address.
+
A routine is nested, if [[Procedural variable|routine variables]] tagged with the modifier <syntaxhighlight lang="delphi" inline>is nested</syntaxhighlight> can store its address.
 
Nevertheless, a [[Global variables|global variable]] is always allowed as a control variable.
 
Nevertheless, a [[Global variables|global variable]] is always allowed as a control variable.
  
 
==== immutable requirement ====
 
==== immutable requirement ====
 
While inside in a loop, it is imperative not to mess with the loop variable.
 
While inside in a loop, it is imperative not to mess with the loop variable.
Plain assignments – e.g. <syntaxhighlight lang="pascal" enclose="none">controlVariable := 2</syntaxhighlight> – are caught by the compiler reporting “Illegal assignment to for-loop variable "controlVariable"”.
+
Plain assignments – e. g. <syntaxhighlight lang="pascal" inline>controlVariable := 2</syntaxhighlight> – are caught by the compiler reporting “Illegal assignment to for-loop variable "controlVariable"”.
 
However, ''indirect'' manipulations are not prevented:
 
However, ''indirect'' manipulations are not prevented:
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 49: Line 49:
 
end.
 
end.
 
</syntaxhighlight>
 
</syntaxhighlight>
The global variable <syntaxhighlight lang="pascal" enclose="none">i</syntaxhighlight> is modified by calling <syntaxhighlight lang="pascal" enclose="none">foo</syntaxhighlight>.
+
The global variable <syntaxhighlight lang="pascal" inline>i</syntaxhighlight> is modified by calling <syntaxhighlight lang="pascal" inline>foo</syntaxhighlight>.
 
The program runs an [[Infinite loop|infinite loop]].
 
The program runs an [[Infinite loop|infinite loop]].
  
 
==== type restrictions ====
 
==== type restrictions ====
The [[Data type|type]] of <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> has to be enumerable.
+
The [[Data type|type]] of <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> has to be enumerable.
 
Assuming your system operates on [[ASCII]] you can do
 
Assuming your system operates on [[ASCII]] you can do
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 68: Line 68:
  
 
==== other step widths ====
 
==== other step widths ====
Other step widths than <syntaxhighlight lang="pascal" enclose="none">1</syntaxhighlight> or <syntaxhighlight lang="pascal" enclose="none">-1</syntaxhighlight> are not possible by utilizing this syntax.
+
Other step widths than <syntaxhighlight lang="pascal" inline>1</syntaxhighlight> or <syntaxhighlight lang="pascal" inline>-1</syntaxhighlight> are not possible by utilizing this syntax.
You have to use other loop constructs such as [[While|<syntaxhighlight lang="pascal" enclose="none">while</syntaxhighlight>]] … <syntaxhighlight lang="pascal" enclose="none">do</syntaxhighlight> and manually initialize, compare and change the <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight>.
+
You have to use other loop constructs such as [[While|<syntaxhighlight lang="pascal" inline>while</syntaxhighlight>]] … <syntaxhighlight lang="pascal" inline>do</syntaxhighlight> and manually initialize, compare and change the <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight>.
  
 
The matter, whether [[FPC|FreePascal]] could provide a syntax specifying other step widths, came up several times.
 
The matter, whether [[FPC|FreePascal]] could provide a syntax specifying other step widths, came up several times.
In general it was regarded as “syntactic sugar”, though, and in order to stay compatible Pascal's core, or Delphi's extensions, in order to avoid any impetuous decisions, the developers remained rather conservative and rejected any changes in that direction.
+
In general it was regarded as “syntactic sugar”, though, and in order to stay compatible Pascal’s core, or Delphi’s extensions, in order to avoid any impetuous decisions, the developers remained rather conservative and rejected any changes in that direction.
 +
 
 +
<sup>See also {{gitlab|issue|FPC|25549}}.</sup>
  
 
=== limits ===
 
=== limits ===
Line 82: Line 84:
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
This excerpt will print one line with <code>5</code>, though you might be fooled by such thoughts as “5 to 5 – that's zero. The body must never be executed.”
+
This excerpt will print one line with <code>5</code>, though you might be fooled by such thoughts as “5 to 5 – the difference is zero. So the body must never be executed.”
 +
This is in fact wrong.
 +
Not the ''difference'' determines the number of iterations, but the cardinality of the set constructed by the expression <syntaxhighlight lang="pascal" inline>[start..finalValue]</syntaxhighlight>.
 +
This can be an empty set, or in the case of <syntaxhighlight lang="pascal" inline>[5..5]</syntaxhighlight> just the single-element set <math>\left\{5\right\}</math>.
  
 
=== legacy ===
 
=== legacy ===
Assuming no other manipulations were made, after the loop the value of <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> will be <syntaxhighlight lang="pascal" enclose="none">final</syntaxhighlight>, unless the the proper condition was not met from the start, then it is ''undefined'' (remains unchanged).
+
Assuming no other manipulations were made, after the loop the value of <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> will be <syntaxhighlight lang="pascal" inline>final</syntaxhighlight>, unless the the proper condition was not met from the start, then it is ''undefined'' (remains unchanged).
  
In case of empty loops (where the body is never executed), even the <syntaxhighlight lang="pascal" enclose="none">start</syntaxhighlight> value is not loaded.
+
In case of empty loops (where the body is never executed), even the <syntaxhighlight lang="pascal" inline>start</syntaxhighlight> value is not loaded.
 
Rationale:
 
Rationale:
The <syntaxhighlight lang="pascal" enclose="none">controlVariable</syntaxhighlight> exists for usage ''inside'' the loop's body.
+
The <syntaxhighlight lang="pascal" inline>controlVariable</syntaxhighlight> exists for usage ''inside'' the loop’s body.
If the loop's body is not entered, then the value might remain unused.
+
If the loop’s body is not entered, then the value might remain unused.
We generally avoid unused values, i.e. any unnecessary assignment without successive reads.
+
We generally avoid unused values, i. e. any unnecessary assignment without successive reads.
  
 
=== short syntax ===
 
=== short syntax ===
For ''single'' statements writing a surrounding <syntaxhighlight lang="pascal" enclose="none">begin</syntaxhighlight> … <syntaxhighlight lang="pascal" enclose="none">end</syntaxhighlight> block can be skipped resulting in:
+
For ''single'' statements writing a surrounding <syntaxhighlight lang="pascal" inline>begin</syntaxhighlight> … <syntaxhighlight lang="pascal" inline>end</syntaxhighlight> frame can be skipped resulting in:
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
for controlVariable := start to final do
 
for controlVariable := start to final do
Line 100: Line 105:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
It is advised though, to make use of that only in justified cases, where the readability is improved and it is very unlikely the loop is expanded by any additional statement.
 
It is advised though, to make use of that only in justified cases, where the readability is improved and it is very unlikely the loop is expanded by any additional statement.
Too many programmers inadvertently fell for the pitfall before and added a properly indented line forgetting it requires a surrounding <syntaxhighlight lang="pascal" enclose="none">begin</syntaxhighlight> … <syntaxhighlight lang="pascal" enclose="none">end</syntaxhighlight> then, too.
+
Too many programmers inadvertently fell for the pitfall before and added a properly indented line forgetting it requires a surrounding <syntaxhighlight lang="pascal" inline>begin</syntaxhighlight> … <syntaxhighlight lang="pascal" inline>end</syntaxhighlight> then, too.
  
Make it a habit and always accompany <syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight>-loops with <syntaxhighlight lang="pascal" enclose="none">begin</syntaxhighlight> … <syntaxhighlight lang="pascal" enclose="none">end</syntaxhighlight>, leaving the option to possibly eliminate those at a ''later'' stage (but ''not'' during development).
+
Make it a habit and always accompany <syntaxhighlight lang="pascal" inline>for</syntaxhighlight>-loops with <syntaxhighlight lang="pascal" inline>begin</syntaxhighlight> … <syntaxhighlight lang="pascal" inline>end</syntaxhighlight>, leaving the option to possibly eliminate those at a ''later'' stage, prior a complete code freeze (but ''not'' right in the middle of development).
  
 
== loop with elements ==
 
== loop with elements ==
With [[for-in loop|<syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight> <syntaxhighlight lang="pascal" enclose="none">in</syntaxhighlight> loops]] the variable that is changed every iteration represents an element out of a collection.
+
With [[for-in loop|<syntaxhighlight lang="pascal" inline>for</syntaxhighlight> … <syntaxhighlight lang="pascal" inline>in</syntaxhighlight> loops]] the variable that is changed every iteration represents an element out of a collection.
 +
This works on strings, [[Array|arrays]], [[Set|sets]], and any other custom collection that implements the required iterators. Looping over an empty collection simply does nothing.
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
type
 
type
Line 121: Line 127:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
In contrast to other loops, an index variable is not provided.
 
In contrast to other loops, an index variable is not provided.
In the above example <syntaxhighlight lang="pascal" enclose="none">ord(thing)</syntaxhighlight> will return an index, but it has to be ''additionally'' retrieved while it inherently exists yet still inaccessible.
+
In the above example <syntaxhighlight lang="pascal" inline>ord(thing)</syntaxhighlight> will return an index, but it has to be ''additionally'' retrieved while it inherently exists yet still inaccessible.
 
[[for-in loop#Proposed extensions|Proposals]] were made, whether and how to extend the syntax allowing to specify an index variable that is adjusted with every iteration.
 
[[for-in loop#Proposed extensions|Proposals]] were made, whether and how to extend the syntax allowing to specify an index variable that is adjusted with every iteration.
  
 
== special commands ==
 
== special commands ==
Inside loops (including <syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight>-loops) two special commands tamper with the regular loop-program-flow.
+
Inside loops (including <syntaxhighlight lang="pascal" inline>for</syntaxhighlight>-loops) two special commands tamper with the regular loop-program-flow.
{{Doc|package=RTL|unit=system|identifier=continue|text=<syntaxhighlight lang="pascal" enclose="none">system.continue</syntaxhighlight>}} skips the rest of the statements in ''one'' iteration.
+
{{Doc|package=RTL|unit=system|identifier=continue|text=<syntaxhighlight lang="pascal" inline>system.continue</syntaxhighlight>}} skips the rest of the statements in ''one'' iteration.
Effectively in a <syntaxhighlight lang="pascal" enclose="none">for</syntaxhighlight>-loop the next element/index is immediately assigned to the control variable, the regular check is performed, and processing of all the statements might start from the top again.
+
Effectively in a <syntaxhighlight lang="pascal" inline>for</syntaxhighlight>-loop the next element/index is immediately assigned to the control variable, the regular check is performed, and processing of all the statements might start from the top again.
Additionally [[Break|<syntaxhighlight lang="pascal" enclose="none">system.break</syntaxhighlight>]] instantly skips the whole loop altogether.
+
Additionally [[Break|<syntaxhighlight lang="pascal" inline>system.break</syntaxhighlight>]] instantly skips the whole loop altogether.
This is different to {{Doc|package=RTL|unit=system|identifier=exit|text=<syntaxhighlight lang="pascal" enclose="none">system.exit</syntaxhighlight>}}, which exits the whole frame, but loops do not create frames (but [[Block|blocks]] do).
+
This is different to {{Doc|package=RTL|unit=system|identifier=exit|text=<syntaxhighlight lang="pascal" inline>system.exit</syntaxhighlight>}}, which exits the whole frame, but loops do not create frames (but [[Block|blocks]] do).
  
Their usage however is usually discredited, since they “disqualify” the loop's iteration condition.
+
Their usage however is usually discredited, since they “disqualify” the loop’s iteration condition.
One has to ''know'' a loop's body contains such commands, in order to actually determine how often a loop is executed.
+
One has to ''know'' a loop’s body contains such commands, in order to actually determine how often a loop is executed.
Without such, you can tell how many times a loop is run just by inspecting the loop's head or tail respectively.
+
Without such, you can tell how many times a loop is run just by inspecting the loop’s head or tail respectively.
  
 
== see also ==
 
== see also ==
 
* [[Example: Why the loop variable should be of signed type]]
 
* [[Example: Why the loop variable should be of signed type]]
* [[sRangechecks|<syntaxhighlight lang="pascal" enclose="none">{$rangechecks}</syntaxhighlight>]]
+
* [[sRangechecks|<syntaxhighlight lang="pascal" inline>{$rangechecks}</syntaxhighlight>]]
  
 
{{Keywords}}
 
{{Keywords}}

Latest revision as of 21:48, 16 July 2022

Deutsch (de) English (en) français (fr) русский (ru)

for is a keyword used in conjunction with other keywords to create loops.

loop with iterator variable

for used along with to/downto and do constructs a loop in which the value of a control variable is incremented or decremented by 1 passing every iteration.

behavior

for controlVariable := start to finalValue do
begin
	statement;
end;

In this example controlVariable is first initialized with the value of start (but cmp. § “legacy” below). If and as long as controlVariable is not greater than finalValue, the begin … end frame with all its statements is executed. By reaching end controlVariable is incremented by 1 and the comparison is made, whether another iteration, whether the statement-frame is executed again.

reverse direction

By exchanging to with downto, the variable – in the example controlVariable – is decremented by 1 and the condition becomes “as long as the control variable is not less than the final value.”

constraints

scope requirements

The control variable has to be local inside nested routines. A routine is nested, if routine variables tagged with the modifier is nested can store its address. Nevertheless, a global variable is always allowed as a control variable.

immutable requirement

While inside in a loop, it is imperative not to mess with the loop variable. Plain assignments – e. g. controlVariable := 2 – are caught by the compiler reporting “Illegal assignment to for-loop variable "controlVariable"”. However, indirect manipulations are not prevented:

program iteratorManipulation(input, output, stderr);

var
	i: longint;

procedure foo;
begin
	i := 1;
end;

begin
	for i := 0 to 2 do
	begin
		writeLn(i);
		foo;
	end;
end.

The global variable i is modified by calling foo. The program runs an infinite loop.

type restrictions

The type of controlVariable has to be enumerable. Assuming your system operates on ASCII you can do

var
	c: char;
begin
	for c := 'a' to 'z' do
	begin
		writeLn(c);
	end;
end.

or generally use any type that is enumerable.

other step widths

Other step widths than 1 or -1 are not possible by utilizing this syntax. You have to use other loop constructs such as while … do and manually initialize, compare and change the controlVariable.

The matter, whether FreePascal could provide a syntax specifying other step widths, came up several times. In general it was regarded as “syntactic sugar”, though, and in order to stay compatible Pascal’s core, or Delphi’s extensions, in order to avoid any impetuous decisions, the developers remained rather conservative and rejected any changes in that direction.

See also FPC issue 25549.

limits

Note, as it is common in mathematics when writing sums [math]\displaystyle{ \sum_{n=0}^{k} }[/math] or products [math]\displaystyle{ \prod_{n=1}^{k} }[/math] the limits are inclusive.

for i := 5 to 5 do
begin
	writeLn(i);
end;

This excerpt will print one line with 5, though you might be fooled by such thoughts as “5 to 5 – the difference is zero. So the body must never be executed.” This is in fact wrong. Not the difference determines the number of iterations, but the cardinality of the set constructed by the expression [start..finalValue]. This can be an empty set, or in the case of [5..5] just the single-element set [math]\displaystyle{ \left\{5\right\} }[/math].

legacy

Assuming no other manipulations were made, after the loop the value of controlVariable will be final, unless the the proper condition was not met from the start, then it is undefined (remains unchanged).

In case of empty loops (where the body is never executed), even the start value is not loaded. Rationale: The controlVariable exists for usage inside the loop’s body. If the loop’s body is not entered, then the value might remain unused. We generally avoid unused values, i. e. any unnecessary assignment without successive reads.

short syntax

For single statements writing a surrounding begin … end frame can be skipped resulting in:

for controlVariable := start to final do
	statement;

It is advised though, to make use of that only in justified cases, where the readability is improved and it is very unlikely the loop is expanded by any additional statement. Too many programmers inadvertently fell for the pitfall before and added a properly indented line forgetting it requires a surrounding begin … end then, too.

Make it a habit and always accompany for-loops with begin … end, leaving the option to possibly eliminate those at a later stage, prior a complete code freeze (but not right in the middle of development).

loop with elements

With for … in loops the variable that is changed every iteration represents an element out of a collection. This works on strings, arrays, sets, and any other custom collection that implements the required iterators. Looping over an empty collection simply does nothing.

type
	furniture = (chair, desk, bed, wardrobe);
	arrangement = set of furniture;
var
	thing: furniture;
begin
	writeLn('all available pieces of furniture:');
	for thing in arrangement do
	begin
		writeLn(thing);
	end;
end.

In contrast to other loops, an index variable is not provided. In the above example ord(thing) will return an index, but it has to be additionally retrieved while it inherently exists yet still inaccessible. Proposals were made, whether and how to extend the syntax allowing to specify an index variable that is adjusted with every iteration.

special commands

Inside loops (including for-loops) two special commands tamper with the regular loop-program-flow. system.continue skips the rest of the statements in one iteration. Effectively in a for-loop the next element/index is immediately assigned to the control variable, the regular check is performed, and processing of all the statements might start from the top again. Additionally system.break instantly skips the whole loop altogether. This is different to system.exit, which exits the whole frame, but loops do not create frames (but blocks do).

Their usage however is usually discredited, since they “disqualify” the loop’s iteration condition. One has to know a loop’s body contains such commands, in order to actually determine how often a loop is executed. Without such, you can tell how many times a loop is run just by inspecting the loop’s head or tail respectively.

see also


Keywords: begindoelseendforifrepeatthenuntilwhile