### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: How to make an iterator "IN" for the for loop go backwards?  (Read 770 times)

#### jamie

• Hero Member
• Posts: 6529
##### How to make an iterator "IN" for the for loop go backwards?
« on: July 16, 2024, 04:17:43 am »

using generics that have enumerators in it, in the FOR loop control, how to go backwards ?

For example:

For MyObject In  MyList downto do .....

Something of that nature.
The only true wisdom is knowing you know nothing

#### Wallaby

• Jr. Member
• Posts: 95
##### Re: How to make an iterator "IN" for the for loop go backwards?
« Reply #1 on: July 16, 2024, 04:33:57 am »
You'd have to implement a custom enumerator, see this sample for delphi: https://www.thedelphigeek.com/2007/03/fun-with-enumerators-part-4-external.html

It's quite complicated, better use
Code: Pascal  [Select][+][-]
1. for I := List.Count - 1 downto 0 do

• Hero Member
• Posts: 15557
• Censorship about opinions does not belong here.
##### Re: How to make an iterator "IN" for the for loop go backwards?
« Reply #2 on: July 16, 2024, 06:45:38 am »
You can indeed write an enumerator for it, and in this case not difficult, but you can do this, much easier:
Code: Pascal  [Select][+][-]
1. {\$mode delphi}
2. uses
3.   Generics.Collections;
4.
5. var
6.   List: TList<Integer>;
7.   Item: Integer;
8. begin
9.   List := TList<Integer>.Create;
10.   try
11.     // Populate the list with elements (you can replace Integer with your desired type T)
15.     // Reverse the list
16.     List.Reverse;
17.     For item in list do
18.      writeln(item);
19.   finally
20.     List.Free;
21.   end;
22. end.
Here for in do works the same, but on the reversed list.
Will add simple enumerator example later. It is not difficult.
« Last Edit: July 16, 2024, 06:48:44 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

#### Warfley

• Hero Member
• Posts: 1528
##### Re: How to make an iterator "IN" for the for loop go backwards?
« Reply #3 on: July 16, 2024, 10:47:06 am »
An Enumerator has two methods, get the current element (Current) and go to the next element (MoveNext). It does not know about the total state, whats the last element, or go back, etc.

The reason for this is very simple, iterators may be used on non container elements. For example you could have an infinite stream, which has no last element and then you obviously can't reverse it. Another point would be online generators, which generate a new element with each call, so it would be highly inefficient to have to store all previous values.

For example:
Code: Pascal  [Select][+][-]
2.
3. type
4.   TFibonacciEnumerator = record
5.     First, Second: Integer;
6.     property Current: Integer read Second;
7.     function MoveNext: Boolean;
8.     function GetEnumerator: TFibonacciEnumerator;
9.   end;
10.
11. function TFibonacciEnumerator.MoveNext: Boolean;
12. var
13.   tmp: Integer;
14. begin
15.   tmp := First + Second;
16.   First := Second;
17.   Second := tmp;
18.   Result := Second >= First;
19. end;
20.
21. function TFibonacciEnumerator.GetEnumerator: TFibonacciEnumerator;
22. begin
23.   Result := Self;
24. end;
25.
26. function Fibonacci: TFibonacciEnumerator;
27. begin
28.   Result.First:=0;
29.   Result.Second:=1;
30. end;
31.
32. var
33.   i: Integer;
34. begin
35.   for i in Fibonacci do
36.     WriteLn(i);
37. end.

#### Warfley

• Hero Member
• Posts: 1528
##### Re: How to make an iterator "IN" for the for loop go backwards?
« Reply #4 on: July 16, 2024, 10:56:13 am »
To add to this, you can easiely write a generic iterator that works with most RTL container classes:
Code: Pascal  [Select][+][-]
1. {\$Mode Delphi}
3.
4. uses
5.  Classes;
6.
7. type
8.
9.   { TReverseIterator }
10.
11.   TReverseIterator<T, TElement> = record
12.   public
13.     Container: T;
14.     CurrentIndex: SizeInt;
15.     function MoveNext: Boolean;
16.     function GetEnumerator: TReverseIterator<T, TElement>;
17.     function GetCurrent: TElement;
18.
19.     property Current: TElement read GetCurrent;
20.   end;
21.
22. function TReverseIterator<T, TElement>.GetCurrent: TElement;
23. begin
24.   Result := Container[CurrentIndex];
25. end;
26.
27. function TReverseIterator<T, TElement>.MoveNext: Boolean;
28. begin
29.   Dec(CurrentIndex);
30.   Result := CurrentIndex >= 0;
31. end;
32.
33. function TReverseIterator<T, TElement>.GetEnumerator: TReverseIterator<T, TElement>;
34. begin
35.   Result := Self;
36. end;
37.
38. function IterReverse<T, TElement>(Container: T): TReverseIterator<T, TElement>;
39. begin
40.   Result.Container := Container;
41.   Result.CurrentIndex := Container.Count;
42. end;
43.
44. var
45.   sl: TStringList;
46.   s: String;
47. begin
48.   sl:=TStringList.Create;
49.   try
53.     for s in IterReverse<TStringList, String>(sl) do
54.       WriteLn(s);
55.   finally
56.     sl.Free;
57.   end;
59. end.

Or to make it more comfortable to use, use a typehelper:
Code: Pascal  [Select][+][-]
1. { ... TReverseIterator definition from above ...}
2.
3. type
4.   TMySLHelper = class helper for TStringList
5.     function Reversed: TReverseIterator<TStringList, String>;
6.   end;
7.
8. function TMySLHelper.Reversed: TReverseIterator<TStringList, String>;
9. begin
10.   Result := IterReverse<TStringList, String>(Self);
11. end;
12.
13. var
14.   sl: TStringList;
15.   s: String;
16. begin
17.   sl:=TStringList.Create;
18.   try
22.     for s in sl.Reversed do
23.       WriteLn(s);
24.   finally
25.     sl.Free;
26.   end;
28. end.
« Last Edit: July 16, 2024, 11:00:13 am by Warfley »