You have read about coroutines. I remember learning it in a programming theory
and languages courses.
When
you make a function call, you jump to that function, execute it and return back to
the calling
function.
A recursive call is where a function call?s itself, however, you complete the instructions
in the
called
function?s context (stack), before returning to the execution of instructions in the
caller?s context.
A coroutine is where the control flows between a pair of methods back and forth as shown here:
In
the next version of C# (2005) the yield statement
facilitates this. The yield statement in C# comes
with
some restrictions. It is used as part of the iterator. You can use it in methods that return an iterator (Enumerator)
or Enumerable.
When
the yield statement is reached the control transfers
to the caller of the method. When the caller
iterates
over to the next item in the collection, the statement after the yield (if
any) is executed in the
method.
The syntax of yield is
yield return whatever;
or
yield break;
Let?s look at an example of using yield in
C#. Assume a method GetValue1() is going to create
a series of
numbers and you are interested in displaying these numbers. The code may be written as shown below:
public static List<int>
GetValues1(int number)
{
List<int>
result = new List<int>();
for (int i = 0;
i <= number; i++)
{
result.Add(i);
//
Delay to simulate some compute intense operation
System.Threading.Thread.Sleep(100);
}
return result;
}
I can call this method like the example below:
foreach (int val in GetValues1(10))
{
Console.Write(val + "
");
}
Console.WriteLine();
When you execute this code,
you will notice that there is a delay (of 1 second) and then the output displays
numbers 0 to 10. What if
you don?t want to delay the output of the result, that is, you want to display the
numbers as they are being
generated. Of course, I could put the display in the GetValue1() method
itself,
but that would be wrong.
What if I don?t want to display but do something else with the values being
generated? The code that
generates the values (GetValue1()) must be separate
from the code that uses it. This
is where yield can help. Let?s implement the GetValue1() method differently. I am naming this newer version
GetValue2():
public static IEnumerable<int>
GetValues2(int number)
{
for (int i = 0;
i <= number; i++)
{
yield return i;
//
Delay to simulate some compute intense operation
System.Threading.Thread.Sleep(100);
}
}
In this example, I am looping
through the numbers and as I generate a value (in this example, trivially, the
number generated is the
index of the loop itself), I return that number with a yield.
This differs from the
regular return in
that you are not returning back to the caller so as to never come back. Once the caller
processes
the returned value, the
code continues to execute any statement after the yield. Here is how I would use this
code:
foreach (int val in GetValues2(10))
{
Console.Write(val + "
");
}
Console.WriteLine();
You see no difference in
how you use the two methods GetValue1() and GetValue2().
However, the behavior is
different. In this case,
instead of a 1 second delay, after a 100ms delay, the first number 0 is displayed,
followed by the second
number after another 100ms delay and so on.
Click here to see a simulation of the execution of above code.
>
I will discuss more about
yield in a future article at the download page.
The yield statement
in Ruby is a lot more elegant. Here is the above example (GetValue2())
written in Ruby:
def
GetValue2(number)
for i in 0..number
yield i
sleep(0.1)
end
end
puts ?Calling GetValue2?
GetValue2(10) { |val| print val.to_s + '
'}
The call to GetValue2() is
given a parameter of 10. The {} following that is the statement that will be
executed for each call
to the yield statement within GetValue2().
The value that is returned by yield, that is i,
is referred
in the caller side as val (declared within the ||).
Click here for the
output from the above code.