Do you yield? Fancy Ruby stuff done in C#, part 4: yield differencies

Please read this post for my reasons behind this article series.

There are few things more aggravating – at least as a programmer working with multiple languages – than when a certain construction is implemented in two extremely different ways. I tend to mix them up and often try using the idioms from one language in the other.

One of these is the yield keyword, which is a very strong construction in functional programming. It exists in both Ruby and C# but works quite differently.

Let’s take Ruby first. yield basically breaks execution, and calls an anonymous code block that have been supplied to the function containing the yield statement. yield can take arguments as well.

yield is quite powerful to create enumerators. Let’s try to make the classic Fibonacci sequence as an “endless” enumerator. And then do the same in C#. Both with yield and look at the differences.

Ruby:

fib = Enumerator.new do |obj|
  obj.yield i = 0
  obj.yield j = 1
  while true
    k = i + j
    obj.yield k
    i = j
    j = k
  end
end

20.times { puts fib.next() }

C#:

namespace fib
{
    class Program
    {
        static public IEnumerable<int> Fibs(int size)
        {
            int i = 0;
            yield return i;
            int j = 1;
            yield return j;

            for (int n = 0; n < size; n++)
            {
                int k = i + j;
                yield return k;
                i = j;
                j = k;
            }
        }

        static void Main(string[] args)
        {
            var fibs = Fibs(20);

            foreach (var f in fibs)
            {
                Console.WriteLine(f);
            }
       }
    }
}

When you look at the two examples, there are some major differencies. In Ruby, the yield statement immediatly breaks execution of the code back to the caller's code-block (in this example the Enumerator itself). A state-machine stores all information about what was going on until the yield. So when the enumerator's next() method is called, the execution continues where it left off. This is why the loop in our Fibonacci generator can be infinite.

In C#, however, IEnumerable will run the Fibs function to the end. Each yield return statement will store its argument inside the Enumerable object, which can then later be iterated over. But that also means that you need to make sure that the generating loop exists at some point - hence the size argument.

Skriv et svar

Din e-mailadresse vil ikke blive offentliggjort. Krævede felter er markeret med *