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.
What do I think? I think that is really cool.
ReplyDeleteIngenious!
ReplyDeleteSimilar stuff :)
ReplyDeletehttp://www.delphi-forum.de/viewtopic.php?t=94597
Oh damn, now you got me, no link to your original implementation.
ReplyDeleteBasically I went a step further eliminating most assembler code and adding the option to rely on threads instead of fibers.
But I'm not trying to steal your credits anyway, lots of your work inspired me. :D
I have successfully implemented your solution in a test project and am pleased with the results.
ReplyDeleteThere is something odd, however; The IDE complains that TDelegateEnumerable does not exist. Despite this the project compiles and runs perfectly under all configurations and, perhaps odder still the code completion lists the class as I type.
Any thoughts?
Hi Hugh,
Deleteif with "The IDE complains" you mean Error Insight just ignore it. This feature is so broken with generics that it is better to turn it off instead of wondering about all the red squiggly lines.
Also please be aware that I recently changed the name to TYieldEnumerable so its use is more clear.
Ok, I am new to XE2 so was not aware of that.
DeleteThe solution works very sweetly.