-
Notifications
You must be signed in to change notification settings - Fork 7
TBidirectionalIterator
Ivan Semenkov edited this page Jan 30, 2021
·
1 revision
Bidirectional iterators are iterators that can be used to access the sequence of elements in a range in the forward and backward directions.
uses
utils.enumerate;
type
generic TBidirectionalIterator<V, Iterator> = class
({$IFDEF FPC}specialize{$ENDIF} TForwardIterator<V, Iterator>)
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>;
Return true if iterator has correct value.
function HasValue : Boolean; virtual; abstract;
Retrieve the next entry.
function Next : Iterator; virtual; abstract;
Retrieve the previous entry.
function Prev : Iterator; virtual; abstract;
Return True if we can move to next element.
function MoveNext : Boolean; virtual; abstract;
Return enumerator for in operator.
function GetEnumerator : Iterator; virtual; abstract;
Get item value.
function GetValue : {$IFNDEF USE_OPTIONAL}V{$ELSE}TOptionalValue
{$ENDIF}; virtual; abstract;
Return current item iterator and move it to next.
function GetCurrent : {$IFNDEF USE_OPTIONAL}V{$ELSE}TOptionalValue
{$ENDIF}; virtual; abstract;
Example how to create bidirectional 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;
{ Retrieve the previous entry in a list. }
function Prev : TIterator; override;
begin
Result := TIterator.Create(FArray, FLength, FPosition - 1);
if TIterator(Result).FPosition < 0 then
begin
TIterator(Result).FPosition := 0;
end;
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;