Recent

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

jamie

  • Hero Member
  • *****
  • Posts: 6735

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

  • Full Member
  • ***
  • Posts: 104
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

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • 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)
  12.     List.Add(1);
  13.     List.Add(2);
  14.     List.Add(3);
  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: 1762
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][+][-]
  1. {$ModeSwitch advancedrecords}
  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: 1762
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}
  2. {$ModeSwitch advancedrecords}
  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
  50.     sl.Add('Test1');
  51.     sl.Add('Test2');
  52.     sl.Add('Test3');
  53.     for s in IterReverse<TStringList, String>(sl) do
  54.       WriteLn(s);
  55.   finally
  56.     sl.Free;
  57.   end;
  58.   ReadLn;
  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
  19.     sl.Add('Test1');
  20.     sl.Add('Test2');
  21.     sl.Add('Test3');
  22.     for s in sl.Reversed do
  23.       WriteLn(s);
  24.   finally
  25.     sl.Free;
  26.   end;
  27.   ReadLn;
  28. end.
« Last Edit: July 16, 2024, 11:00:13 am by Warfley »

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • Censorship about opinions does not belong here.
Re: How to make an iterator "IN" for the for loop go backwards?
« Reply #5 on: July 16, 2024, 01:35:16 pm »
The latter is imho less comfortable, because the helper is typed.
The generic one before that is quite similar to what I wrote based on the Halvar Vastbotn code. (that on its own not yet works in fpc, because of nested generics.)
If I smell bad code it usually is bad code and that includes my own code.

 

TinyPortal © 2005-2018