Saturday, March 10, 2007

Fun with enumerators, part 6 - generators

[Part 1 - Introduction, Part 2 - Additional enumerators, Part 3 - Parameterized enumerators, Part 4 - External enumerators, Part 5 - Class helper enumerators.]

This was a long and interesting trip but it has to end some day. And it will end today. Just one last small topic to cover ...

in Part 4 I mentioned that we could abuse enumerators to the point where they are not working on any external structure. My example for such generator which is providing its own enumeration data was

for i in Fibonacci(10) do

and I promised to show you how to write it.

Again, we will start with a 'standard' enumerator factory, the one that we used a lot in parts 4 and 5.

  IEnumFibonacciFactory = interface
function GetEnumerator: TEnumFibonacci;

TEnumFibonacciFactory = class(TInterfacedObject, IEnumFibonacciFactory)
FUpperBound: integer;
constructor Create(upperBound: integer);
function GetEnumerator: TEnumFibonacci;

function Fibonacci(upperBound: integer): IEnumFibonacciFactory;

Enumerator is slightly trickier this time - it prepares requested data in the constructor and uses MoveNext to move over this data.

  TEnumFibonacci = class
FFibArray: array of integer;
FIndex: integer;
constructor Create(upperBound: integer);
function GetCurrent: integer;
function MoveNext: boolean;
property Current: integer read GetCurrent;

constructor TEnumFibonacci.Create(upperBound: integer);
i: integer;
SetLength(FFibArray, upperBound);
if upperBound >= 1 then
FFibArray[0] := 1;
if upperBound >= 2 then
FFibArray[1] := 1;
for i := 2 to upperBound - 1 do
FFibArray[i] := FFibArray[i-1] + FFibArray[i-2];
FIndex := -1;

function TEnumFibonacci.GetCurrent: integer;
Result := FFibArray[FIndex];

function TEnumFibonacci.MoveNext: boolean;
Result := FIndex < High(FFibArray);
if Result then

Test code follows the well-learned pattern.

procedure TfrmFunWithEnumerators.btnFibonacciClick(Sender: TObject);
i : integer;
ln: string;
ln := '';
for i in Fibonacci(10) do begin
if ln <> '' then
ln := ln + ', ';
ln := ln + IntToStr(i);
lbLog.Items.Add('Generator: ' + ln);

And now it really is a time to say goodbye. At least to this series - I intend to write many more blog entries. It was an interesting experience for me and I hope an interesting reading for you, dear reader. If you liked it, tell that to others so that they may enjoy it too.

[A full source code of the demo program including all enumerators is available at]

Technorati tags: , ,

Labels: , , ,


Anonymous Anonymous said...

It would be nice to a have a article which contains all blog article in one article

Blogger gabr said...

Are you sure? It would be rather lenghty.

Truth is, I'm a magazine writer in first place and blogger only in second. If 'Fun with enumerators' would be a magazine article, each post with be written under one subheading of this article. But on the web, short form is better - or at least I think it is.

What if I combine all posts into one PDF file?

What do other readers think?

Blogger renaud75 said...

Usually I avoid heavy PDF when Googling for technical information.
But I aggree that navigation in a blog is not always optimal.

May be publishing somme part in i.e "Torry's Delphi Pages tips" or other sites may help to advertise your very didactic topic on enumerator.

Blogger Chris said...

On the article debate - personally, I think the blog form is perfect for this sort of thing.

That said, just on the code - in the current sort of case, do you actually need a separate factory class? At least, would the following work? (I don't have D2006/7 to check):

IFibonacciEnum = interface
function GetCurrent: Integer;
function MoveNext: Boolean;
property Current: Integer read GetCurrent;

IGetFibonacciEnum = interface
function GetEnumerator: IFibonacciEnum;

TFibonacciEnum = class(TInterfacedObject, IFibonacciEnum, IGetFibonacciEnum)
FFibArray: array of Integer;
FIndex: Integer;
function GetEnumerator: IFibonacciEnum;
function GetCurrent: Integer;
function MoveNext: Boolean;
constructor Create(UpperBound: Integer);

function TFibonacciEnum.GetEnumerator: IFibonacciEnum;
Result := Self;

// of your code for Create, GetCurrent and MoveNext

function Fibonacci(UpperBound: Integer): IGetFibonacciEnum;
Result := TFibonacciEnum.Create(UpperBound);

Blogger gabr said...

Good idea! Of course the enumerator can be its own factory.

I tested your code in BDS 2006 and it works fine.

Anonymous Anonymous said...


I've read all of your posting here with enumerators and I like it very much. I was not aware that is possible.
Anyway, may I ask for help? I've existing code which I would like to enhance using your tips but before starting to rebuild everything I would like to get some opinion. I've shorten the coding and remove some of the properties but the main purpose is visible, I guess:

TPWCNoticeItem = class(TCollectionItem)
property NoticeID : TPWCId read FId write FId;

TPWCNoticeList = class(TCollection)
function Get(IDX : Integer): TPWCNoticeItem;
procedure Put(IDX: Integer; const ITEM : TPWCNoticeItem);
constructor Create;
function Add : TPWCNoticeItem;
property Info[IDX : Integer] : TPWCNoticeItem read Get write Put; default;
destructor Destroy; override;

So where would you start to rebuild this coding or better is it even possible?
Perhaps you could give me a starting point? It must not be a complete coding but some insights would be really helpful.


Blogger gabr said...

I assume you want to iterate over TPWCNoticeList and return TPWCNoticeItems? IOW, you want to do this:

item: TPWCNoticeItem;
list: TPWCNoticeList;

// create and fill the 'list' somehow
for item in list do
// do something with item


First you have to add a public GetEnumerator function to the TPWCNoticeList class. It must return some class responsible for enumeration (TPWCNoticeListEnumerator, for example).

Then you have to create this TPWCNoticeListEnumerator class and implement MoveNext, GetCurrent and Current in it - just like in all my examples.

GetEnumerator would create a new instance of the TPWCNoticeListEnumerator class while passing 'Self' to the constructor. TPWCNoticeListEnumerator would store this reference to the TPWCNoticeList class so it can be used for enumeration.

You can check how the enumerator for TGpIntegerList is implemented in my GpLists unit.

Anonymous Anonymous said...

Thanks for this reply and I got this running w/o any further problems.

Do you know if I can use enumarations/iterations this way, too:
for i := NoticeList.Items.Count -1 downto 0 do [something]

As the help does not mention it, I guess it is not possible at all, right?


Blogger gabr said...

If I understand correctly, you want to write enumerator that will walk through your list in reverse order?

No problems here - just initialize your enumeration class (TPWCNoticeListEnumerator) to the end of the list and then proceed towards beginning in MoveNext. I did something very similar in Part 4.

If you need both enumerators - one forward and one reverse - implement the latter via additional property, as I did in Part 2.


Post a Comment

Links to this post:

Create a Link

<< Home