Skip to content

TForwardIterator

Ivan Semenkov edited this page Jan 30, 2021 · 1 revision

Table of contents

About

Forward iterators are iterators that can be used to access the sequence of elements in a range in the direction that goes from its beginning towards its end.

uses
  utils.enumerate;
  
type
  generic TForwardIterator<V, Iterator> = class

TOptionalValue

If macro {$USE_OPTIONAL} is defined, then all methods return a TOptionalValue wrapper, otherwise V.

uses
  utils.optional;

type
  TOptionalValue = {$IFDEF FPC}specialize{$ENDIF} TOptional<V>;

HasValue

Return true if iterator has correct value.

function HasValue : Boolean; virtual; abstract;

Next

Retrieve the next entry.

function Next : Iterator; virtual; abstract;

MoveNext

Return True if we can move to next element.

function MoveNext : Boolean; virtual; abstract;

GetEnumerator

Return enumerator for in operator.

function GetEnumerator : Iterator; virtual; abstract;

GetValue

Get item value.

function GetValue : {$IFNDEF USE_OPTIONAL}V{$ELSE}TOptionalValue
  {$ENDIF}; virtual; abstract;

GetCurrent

Return current item iterator and move it to next.

function GetCurrent : {$IFNDEF USE_OPTIONAL}V{$ELSE}TOptionalValue
  {$ENDIF}; virtual; abstract;

Example

Example how to create forward iterator for dynamic integer array.

uses
  SysUtils{$IFDEF USE_OPTIONAL}, utils.optional{$ENDIF}, utils.enumerate;
  
type
  TIterator = class; { Fix for FreePascal compiler. }
  TIterator = class ({$IFDEF FPC}specialize{$ENDIF}
    TForwardIterator<Integer, TIterator>)
  public
    {$IFNDEF USE_OPTIONAL}
      { Array index is not exists. }
      EIndexOutOfRangeException = class(Exception);
    {$ELSE}
      TOptionalValue = {$IFDEF FPC}specialize{$ENDIF} TOptional<Integer>;
    {$ENDIF}
    PDynIntegerArray = ^TDynIntegerArray;
    TDynIntegerArray = array of Integer;
  protected
    FArray : PDynIntegerArray;
    FLenght : LongInt;
    FPosition : LongInt;
  public
    { Create new iterator for dynamic array. }
    constructor Create (Arr : PDynIntegerArray; ALen : Cardinal; APos : Integer);
    begin
      FArray := Arr;
      FLenght := ALen;
      FPosition := APos;
    end;
    
    { Return true if iterator has correct value. }
    function HasValue : Boolean; override;
    begin
      if FPosition >= FLenght then
        Exit(False);
      
      Result := True;
    end;
    
    { Retrieve the next entry. }
    function Next : TIterator; override;
    begin
      Result := TIterator.Create(FArray, FLength, FPosition + 1);
    end;
    
    { Return True if we can move to next element. }
    function MoveNext : Boolean; override;
    begin
      Result := FPosition < FLength;
    end;
    
    { Return enumerator for in operator. }
    function GetEnumerator : TIterator; override;
    begin
      Result := TIterator.Create(FArray, FLength, FPosition);
    end;
  protected
    { Get item value. }
    function GetValue : {$IFNDEF USE_OPTIONAL}T{$ELSE}TOptionalValue
      {$ENDIF}; override;
    begin
      if FPosition > FLength then
      begin
        {$IFNDEF USE_OPTIONAL}
          raise EIndexOutOfRangeException.Create('Index out of range.');
        {$ELSE}
          Exit(TOptionalValue.Create);
        {$ENDIF}
      end;

      Result := {$IFDEF USE_OPTIONAL}TOptionalValue.Create({$ENDIF}
        FArray^[FPosition]^.Value{$IFDEF USE_OPTIONAL}){$ENDIF};
    end;
    
    { Set new item value. }
    procedure SetValue (AValue : {$IFNDEF USE_OPTIONAL}Integer{$ELSE}
      TOptionalValue{$ENDIF});
    begin
      if FPosition > FLength then
      begin
        {$IFNDEF USE_OPTIONAL}
          raise EIndexOutOfRangeException.Create('Index out of range.');
        {$ELSE}
          Exit;
        {$ENDIF}
      end;

      FArray^[FPosition]^.Value := AValue{$IFDEF USE_OPTIONAL}.Unwrap{$ENDIF};
    end;
    
    { Return current item iterator and move it to next. }
    function GetCurrent : {$IFNDEF USE_OPTIONAL}T{$ELSE}TOptionalValue
      {$ENDIF}; override;
    begin
      Result := GetValue;
  	  Inc(FPosition);
    end;
  public
    { Read/Write array item value. If value not exists raise 
      EIndexOutOfRangeException. }
    property Value : {$IFNDEF USE_OPTIONAL}T{$ELSE}TOptionalValue{$ENDIF} 
      read GetValue write SetValue;
    property Current : {$IFNDEF USE_OPTIONAL}T{$ELSE}TOptionalValue{$ENDIF}
      read GetCurrent;
      
var
  someArr : array of Integer;
  iterator : TIterator;
  
begin  
  SetLength(someArr, 10);
  iterator := TIterator.Create(@someArr, Length(someArr), 0);
  while iterator.HasValue do
  begin
    writeln(iterator.Value);
    iterator := iterator.Next;
  end;
  
  FreeAndNil(iterator);
end;
Clone this wiki locally