Difference between revisions of "for-in loop"

From Lazarus wiki
m (Traversing container)
(Limitations -> Proposed extensions)
Line 161: Line 161:
 
</delphi>
 
</delphi>
  
=== Limitations ===
+
=== Proposed extensions ===
  
1. It is imposible to traverse a collection:
+
The following examples are not supported by Delphi, and proposed for FPC mode only.
 +
 
 +
In Delphi, it is impossible to traverse neither enumerated nor ranged type:
  
The next code is not valid:
 
 
<delphi>
 
<delphi>
 
type
 
type
Line 171: Line 172:
 
var
 
var
 
   Color: TColor;
 
   Color: TColor;
 +
  ch: Char;
 
begin
 
begin
 
   for Color in TColor do
 
   for Color in TColor do
 
     DoSomething(Color);
 
     DoSomething(Color);
 +
  for ch in 'a'..'z' do
 +
    DoSomethingOther(ch);
 
end.
 
end.
 
</delphi>
 
</delphi>
  
Although you can traverse a set. So the next code is valid:
+
Although you can traverse a set. So the next code is valid even in Delphi:
 
<delphi>
 
<delphi>
 
type
 
type
Line 189: Line 193:
 
</delphi>
 
</delphi>
  
2. It is imposible to choose among different possible enumerators. For example you can traverse a tree using different routs. The well known algorithms are: preorder, postorder, inorder and breadth‑first traversals. Therefore it would be useful to have an ability to choose an enumerator. For example using the next syntax:
+
It is impossible to choose among different possible enumerators. For example you can traverse a tree using different orders. The well known algorithms are: preorder, postorder, inorder and breadth‑first traversals. Therefore it would be useful to have an ability to choose an enumerator. For example using one of the following syntax variants:
  
 
<delphi>
 
<delphi>
Line 197: Line 201:
 
   Node: TNode;
 
   Node: TNode;
 
begin
 
begin
   for Node in Tree using GetEnumerator(teInOrder) do // or 'using GetInOrderEnumerator' or ...
+
   for Node in Tree using GetEnumerator(teInOrder) do // Proposed syntax variant 1
 +
  for Node using Tree.GetInOrderEnumerator do do  // Proposed syntax variant 2
 +
  for Node in GetEnumerator(Tree, teInOrder) do // Proposed syntax variant 3
 
     Dosomething(Node);
 
     Dosomething(Node);
 
end.
 
end.
 
</delphi>
 
</delphi>

Revision as of 08:08, 20 October 2009

"for-in" loop exists in delphi starting from 2005 version.

Delphi implementation

It has the next syntax:

String loop

<delphi> procedure StringLoop(S: String); var

 C: Char;

begin

 for C in S do
   DoSomething(C);

end; </delphi>

Array loop

<delphi> procedure ArrayLoop(A: Array of Byte); var

 B: Byte;

begin

 for B in A do
   DoSomething(B);

end; </delphi>

Set loop

<delphi> type

 TColor = (cRed, cGren, cBlue);
 TColors = set of TColor;

procedure SetLoop(Colors: TColors); var

 Color: TColor;

begin

 for Color in Colors do
   DoSomething(Color);

end; </delphi>

Traversing container

To traverse some container class you need to add an enumerator for it. Enumerator is a class built by the next template:

<delphi> TSomeEnumerator = class public

 function MoveNext: Boolean;
 property Current: TSomeType;

end; </delphi>

There are only 2 things required for the enumerator: MoveNext method which asks enumerator to step forward and property Current which can return any desired type.

Next thing is to add magic GetEnumerator method to the container class which returns an enumerator instance.

For example: <delphi> type

 TEnumerableTree = class;
 TTreeEnumerator = class
 private
   FTree: TEnumerableTree;
   FCurrent: TNode;
 public
   constructor Create(ATree: TEnumerableTree); 
   function MoveNext: Boolean;
   property Current: TNode read FCurrent;
 end;
 TEnumerableTree = class
 public
   function GetEnumerator: TTreeEnumerator;
 end;

constructor TTreeEnumerator.Create(ATree: TEnumerableTree); begin

 inherited Create;
 FTree := ATree;
 FCurrent := nil;

end;

function TTreeEnumerator.MoveNext: Boolean; begin

 // some logic to get the next node from a tree
 if FCurrent = nil then
   FCurrent := FTree.GetFirstNode
 else
   FCurrent := FTree.GetNextNode(FCurrent);
 Result := FCurrent <> FTree.GetLastNode;

end;

function TEnumerableTree.GetEnumerator: TTreeEnumerator; begin

 Result := TTreeEnumerator.Create(Self);

end;

</delphi>

After this you are able to execute the next code:

<delphi> procedure TreeLoop(ATree: TEnumerableTree); var

 ANode: TNode;

begin

 for ANode in ATree do
   DoSomething(ANode);

end; </delphi>

Of course enumerator support is built into the basic classes: TList, TStrings, TCollection, TComponent, ...

For-in loop can be easily translated into the while loop. Two next examples doing same things:

Example 1. <delphi> procedure TraverseStrings(AStrings: TStrings); var

 S: String;

begin

 for S in AStrings do
   DoSomething(S);

end; </delphi>

Example 2. <delphi> procedure TraverseStrings(AStrings: TStrings); var

 S: String;
 Enumerator: TStringsEnumerator;

begin

 Enumerator := AStrings.GetEnumerator;
 while Enumerator.MoveNext do
   DoSomething(Enumerator.Current);

end; </delphi>

It is also possible to make some class enumerable if you implement the next interface for the container: <delphi>

 IEnumerable = interface(IInterface)
   function GetEnumerator: IEnumerator;
 end;

</delphi>

Where IEnumerator is declared as: <delphi>

 IEnumerator = interface(IInterface)
   function GetCurrent: TObject;
   function MoveNext: Boolean;
   procedure Reset;
   property Current: TObject read GetCurrent;
 end;

</delphi>

Proposed extensions

The following examples are not supported by Delphi, and proposed for FPC mode only.

In Delphi, it is impossible to traverse neither enumerated nor ranged type:

<delphi> type

 TColor = (clRed, clBlue, clBlack);

var

 Color: TColor;
 ch: Char;

begin

 for Color in TColor do
   DoSomething(Color);
 for ch in 'a'..'z' do
   DoSomethingOther(ch);

end. </delphi>

Although you can traverse a set. So the next code is valid even in Delphi: <delphi> type

 TColor = (clRed, clBlue, clBlack);

var

 Color: TColor;

begin

 for Color in [clRed..clBlack] do
   DoSomething(Color);

end. </delphi>

It is impossible to choose among different possible enumerators. For example you can traverse a tree using different orders. The well known algorithms are: preorder, postorder, inorder and breadth‑first traversals. Therefore it would be useful to have an ability to choose an enumerator. For example using one of the following syntax variants:

<delphi> type

 TTreeEnumeratorType = (tePreOrder, tePostOrder, teInOrder, teBreadthFirst)

var

 Node: TNode;

begin

 for Node in Tree using GetEnumerator(teInOrder) do // Proposed syntax variant 1
 for Node using Tree.GetInOrderEnumerator do do  // Proposed syntax variant 2
 for Node in GetEnumerator(Tree, teInOrder) do // Proposed syntax variant 3
   Dosomething(Node);

end. </delphi>