Interesting use of declarative coding


Having been using LINQ since it first came out, I feel that I’m only now starting to really appreciate some of the applications for declarative coding. When I first heard about it, I was pretty sceptical about it, but I’m actually becoming a pretty big fan of it now. Here’s an example that I thought about a few days ago.

Someone asked me to write some code that would print out the first five answers of the first five times’ tables e.g.

  • 1 x 1 = 1, 1 x 2 = 2, 1 x 3 = 3, 1 x 4 = 4, 1 x 5 = 5
  • 2 x 1 = 2, 2 x 2 = 4, 2 x 3 = 6, 2 x 4 = 8, 2 x 5 = 10

etc. etc.

I actually initially thought “let’s do this with LINQ” but on the spur of the moment went back to my imperative coding roots and fumbled around with a couple of nested for loops… it probably ended up looking something like this:

(Imagine that limit is a const int of 5)

 

for (int outer = 1; outer <= limit; outer++)
{
    for (int inner = 1; inner <= limit; inner++)
    {
        var answer = outer * inner;
        Console.Write (String.Format ("{0} x {1} = {2}, ", outer, inner, answer));
    }
    Console.WriteLine ();
}

 

(Please ignore the fact that there would be an extra comma at the end of each line…)

Anyway… I got back to thinking about how I could have written this in LINQ and (pretty quickly) ended up with this:

    var numbers = from outer in Enumerable.Range (1, limit)
                  from inner in Enumerable.Range (1, limit)
                  let answer = outer * inner
                  select new
                  {
                      EndOfLine = inner == limit,
                      Text = String.Format ("{0} x {1} = {2}, ", outer, inner, answer)
                  };

    foreach (var number in numbers)
    {
        Console.Write (number.Text);

        if (number.EndOfLine)
            Console.WriteLine ();
    }
}

If you’re not used to working with LINQ queries and / or projection, this may seem a little strange to you. But it’s pretty simple really – the first two lines just build up a 5 x 5 matrix (remember that limit = 5), and then we simply apply the function to create a new anonymous type over it.

Takes up slightly more code, but to my mind, at least from a readability perspective, offers two main benefits over the imperative version: –

  • Clear separation between the logic / construction of the dataset, and the effort of printing them out. You could change the source data without any work required on the bit of code to print it out. Of course you could do that in the first example, but it’s not natural to think of that – when writing with for loops I just seem to naturally mash the two together parts into one.
  • Clarity of program flow. In the second example, it’s clear why we’re printing Console.WriteLine (), compared to the previous example where the WriteLine () is there almost implicitly at the end of the outer for loop. You can figure it out easily enough – but the second example, to me at least conveys the intent better.

I actually also went one step further and took the second example to its logical conclusion and put the number.EndOfLine conditional within the anonymous type itself (as well as the Console.WriteLine bit):

var numbers = from outer in Enumerable.Range (1, limit)
              from inner in Enumerable.Range (1, limit)
              let endOfLine = inner == limit
              let answer = outer * inner
              let text = String.Format ("{0} x {1} = {2}, ", outer, inner, answer)
              select new
              {
                  Print = endOfLine
                  ? (Action) (() => Console.WriteLine (text))
                  : () => Console.Write (text)
              };

foreach (var number in numbers)
    number.Print ();

I’m not sure whether I like this or not – in some ways it’s quite elegant, but the amount of code has risen again, plus I’m not sure whether the practice creating anonymous methods as a result of a conditional, as a property of an anonymous type is really that great an idea vis a vis readability or not 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s