Could you please give me an example for Deferred execution with eager evaluation in C#?
I read from MSDN that deferred execution in LINQ can be implemented either wi
Bellow is my answer but also note that Jon Skeet spoke about it today on his blog an about the fact that he is not totally ok with the MSDN meaning of "Lazy" as MSDN isn't really clear of what lazy exactly mean when they use it in Just how lazy are you ? his post make for an interesting read.
Additionally Wikipedia assume that three rules should be maintained for lazy evaluation and third point isn't respected in MSDN meaning as the expression will be evaluated more than once if GetEnumerator
is called again (By the spec Reset is not implemented on enumerator objects generated using the yield
keyword and most of linq use it currently)
Considering a function
int Computation(int index)
IEnumerable<int> GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for(int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
return result;
}
Computation
is executed maxIndex
timesGetEnumerator
returns a new instance of the enumerator doing nothing more.MoveNext
put the the value stored in the next Array cell in the Current
member of the IEnumerator
and that's all.Cost: Big upfront, Small during enumeration (only a copy)
IEnumerable<int> GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for(int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
foreach(var value in result)
{
yield return value;
}
}
IEnumerable
is created and a copy of the argument (maxIndex
) is stored in it.GetEnumerator
returns a new instance of the enumerator doing nothing more.MoveNext
executes maxIndex times the compute method, store the result in an array and Current
will return the first value.MoveNext
will put in Current
a value stored in the array.Cost: nothing upfront, Big when the enumeration start, Small during enumeration (only a copy)
IEnumerable<int> GetComputation(int maxIndex)
{
for(int i = 0; i < maxIndex; i++)
{
yield return Computation(i);
}
}
GetEnumerator
returns a new instance of the enumerator doing nothing more.MoveNext
execute once the Computation
code, put the value in Current
and let the caller immediately act on the result.Most of linq use deferred and lazy execution but some functions can't be so like sorting.
Cost: nothing upfront, Moderate during enumeration (the computation is executed there)
MoveNext
or when the IEnumerator
instance is created (For IEnumerable
it is when GetEnumerator
is called)MoveNext
is called but nothing before.Parallel LINQ does it a little differently as the computation could be considered deferred/Lazy from the point of view of the caller but internally the computation of some number of elements begin in parallel as soon as the enumeration begin. The result is that if the next value is already there you get it immediately but otherwise you will have to wait for it.
One way that you could eagerly evaluate a deferred execution IEnumerable is to simply turn it into an array using linq's .ToArray() function.
var evaluated = enumerable.ToArray();
This forces evaluation of the full enumerable and then you have the array that you can do whatever you want with.