Sunday, April 17, 2011

Yield return and Delphi

Well, some may know that Sergey Antonov already came up with some implementation on the yield functionality for Delphi in 2007. It was pretty impressive but for my taste it involved to much asm stuff and it was before Delphi had generics.

So what if we could do something like this?

for i in Power(2, 10) do
begin
  Writeln(i);
end;

Well, you can! All you need is define the function as follows:

function Power(ANumber, AExponent: Integer): IEnumerable<Integer>;
begin
  Result := TDelegateEnumerable<Integer>.Create(
    procedure
    var
      i, k: Integer;
      Result: Yield<Integer>;
    begin
      k := 1;
      for i := 1 to AExponent do
      begin
        k := k * ANumber;
        Result := k;
      end;
    end);
end;

Looks pretty much simple and straight forward, eh?

So what happens there? Well basically it creates some TEnumerable<T> descendant which takes a TProc as parameter and executes it when GetEnumerator is called. Well but thats only half the truth. The real magic happens when you assign a value to that Yield<Integer> variable. And that's basically a record with some implicit operator overload which makes the worker thread or fiber (for now you can only set that with a compiler switch in the Collections.Yield.pas) continue and make the Enumerator do his MoveNext and provide the value for the GetCurrent.

I also implemented support for Alex Ciobanu's awesome collection library (you can also set that up with a compiler switch in the Collections.Yield.pas). Then you can do something funny like this (provided that you set the result type of your function to IEnexCollection<T>):

for i in Power(2, 10).Reversed do
begin
  Writeln(i);
end;

Ok, you might say, I can do all that with getting my values and putting them into some list and I can easily go through it with any loop I want. True, but then you will either have to wait until your calculation and processing is done and the list is generated. And also you have to carry around the list and the elements in it. What if you just want to set up some parameters and calculate the result later? Well with that implementation you can since the enumeration procedure is just called when GetEnumerator is called.

So, pretty cool, no? What do you think?

P.S.: The full source code is available on google code as always.