如何实现具有 IsFirst 和 IsLast 函数的 Enumerator?

How to implement a Enumerator that has IsFirst and IsLast functions?

我正在为所有集合使用 Spring4D。

现在有一种情况,我必须知道枚举器的当前值是集合中的第一个(这很容易)还是最后一个(这很难)。

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  Spring.Collections;

var
  Enumerable: IEnumerable<Integer>;
  Enumerator: IEnumerator<Integer>;

begin
  Enumerable := TEnumerable.Query<Integer>(TArray<Integer>.Create(1, 2, 3, 4, 5)
    ) as IEnumerable<Integer>;
  Enumerator := Enumerable.GetEnumerator;
  while Enumerator.MoveNext do
  begin
    WriteLn('Value = ', Enumerator.Current);
    WriteLn('First in collection? ', Enumerator.CurrentIsFirst);
    WriteLn('Last in collection? ', Enumerator.CurrentIsLast);
  end;
  ReadLn;

end.

CurrentIsFirst 可以使用局部布尔值实现,一旦第一个值通过,该布尔值就会被重置。

但是我不知道实现 CurrentIsLast 的简单方法。

它应该能够处理惰性集合,因为它们可能包含太多值以适应内存。

如何实现这样的 CurrentIsLast 功能?

只需在迭代期间使用标志:

if Enumerator.MoveNext then
begin
  flag := True;
  repeat
    WriteLn('Value = ', Enumerator.Current);
    WriteLn('First in collection? ', flag);
    flag := not Enumerator.MoveNext;
    WriteLn('Last in collection? ', flag);
  until flag;
end;

这是基本算法,但您可以将其放入 IEnumerator<T> 的装饰器中以提供 IsFirst/IsLast - 您只需要缓冲当前元素并向前看查看当前是不是最后一个。

type
  IEnumeratorEx<T> = interface(IEnumerator<T>)
    function IsFirst: Boolean;
    function IsLast: Boolean;
  end;

  TEnumeratorState = (Initial, First, Only, Running, Last, Finished);
  TEnumeratorEx<T> = class(TEnumeratorBase<T>, IEnumeratorEx<T>)
  private
    fSource: IEnumerator<T>;
    fCurrent: T;
    fState: TEnumeratorState;
    function IsFirst: Boolean;
    function IsLast: Boolean;
  protected
    function GetCurrent: T; override;
    function MoveNext: Boolean; override;
  public
    constructor Create(const source: IEnumerator<T>);
  end;

constructor TEnumeratorEx<T>.Create(const source: IEnumerator<T>);
begin
  inherited Create;
  fSource := source;
end;

function TEnumeratorEx<T>.GetCurrent: T;
begin
  Result := fCurrent;
end;

function TEnumeratorEx<T>.IsFirst: Boolean;
begin
  Result := fState in [First, Only];
end;

function TEnumeratorEx<T>.IsLast: Boolean;
begin
  Result := fState in [Only, Last];
end;

function TEnumeratorEx<T>.MoveNext: Boolean;
begin
  case fState of
    Initial:
      if fSource.MoveNext then
        fState := First
      else
        fState := Finished;
    First:
      fState := Running;
    Only, Last:
      fState := Finished;
  end;

  Result := fState <> Finished;
  if Result then
  begin
    fCurrent := fSource.Current;
    if not fSource.MoveNext then
      Inc(fState);
  end;
end;