There has been a recent request to add to for loop in the Object Pascal language the ability to add a step, that is an increment different from one. Here is an alternative implementation using a for..in loop and a custom enumerator.

Once I saw the request it reminded me of a demo I have in my Object Pascal Handbook (which originally came from my Delphi 2007 Handbook), The demo is available at https://github.com/MarcoDelphiBooks/ObjectPascalHandbook/ blob/master/10/NumbersEnumerator/NumbersEnumerator_MainForm.pas and shows how to build an enumerator over a range of numeric value. The only missing feature is the ability to have a Step property to skip value in the sequence, counting by 2, 3, 4 and so on. The other issue of the demo is it uses an object of a class, which implies the need of creating and freeing the object. Also the way properties are set is a bit cumbersome.

In the new version (listed below) I have moved from using classes to using records, still keeping the enumeration as a nested type, and added a static class function to initialize the data structure in a single call. The goal is to be able to use it as follows:

procedure TForm1.btnTestClick(Sender: TObject);
var
  I: Integer;
begin
  for I in TNumbersRange.Range (5, 13, 3) do
    Show (IntToStr (I));
end;

Of course, you could also define a global Range () function, rather than a class method.

Below is an image with the type definition, followed by a second one with the core implementation. They are followed by the code itself, which might not be as well formatted and readable (hence the images). Feel free to use it any way you like. 

 

Here is the record declaration and definition in text format:

type
  TNumbersRange = record
  public

  type
    TNumbersRangeEnum = record
    private
      nPos: Integer;
      nStart: Integer;
      nEnd: Integer;
      nStep: Integer;
    public
      constructor Create (aRange: TNumbersRange);
      function MoveNext: Boolean;
      function GetCurrent: Integer;
      property Current: Integer read GetCurrent;
    end;

  private
    NStart: Integer;
    NEnd: Integer;
    NStep: Integer;

  public
    class function Range (aStart, aEnd, aStep: Integer): TNumbersRange; static;
    function GetEnumerator: TNumbersRangeEnum;
  end;

implementation

uses
  Math;

class function TNumbersRange.Range (aStart, aEnd, aStep: Integer): TNumbersRange;
begin
  Result.NStart := aStart;
  Result.NEnd := aEnd;
  Result.NStep := aStep;
end;

{ TNumbersRange }

function TNumbersRange.GetEnumerator: TNumbersRangeEnum;
begin
  Result := TNumbersRangeEnum.Create (self);
end;

constructor TNumbersRange.TNumbersRangeEnum.
  Create(aRange: TNumbersRange);
begin
  nStart := aRange.NStart;
  nEnd := aRange.NEnd;
  nStep := IfThen (aRange.NStep = 0, 1, aRange.NStep);
  nPos := aRange.nStart - nStep;
end;

function TNumbersRange.TNumbersRangeEnum.
  GetCurrent: Integer;
begin
  Result := nPos;
end;

function TNumbersRange.TNumbersRangeEnum.
  MoveNext: Boolean;
begin
  Inc (nPos, nStep);
  Result := nPos <= nEnd;
end;