Geeks With Blogs
New Things I Learned

I was aware of lambda expressions because I was reading about them in the C# 3.0 white paper specification, along with other new language features like extension methods, anonymous types, basis for LINQ, etc.  I thought it was cool enough, it makes passing anonymous delegates much better in terms of code writing/reading.  However, actually using them takes a bit of getting used to.  As such, I'd like to give a short example as to how I visualize writing lambda expressions.

Consider the following class structure representing a sports organization:

public class League

{

   private List<Conference> _conferences;

 

   public IList<Conference> Conferences

   {

      get { return _conferences; }

   }

}

 

public class Conference

{

   private List<Division> _divisions;

 

   public IList<Division> Divisions

   {

      get { return _divisions; }

   }

}

 

public class Division

{

   private List<Team> _teams;

 

   public IList<Team> Teams

   {

      get { return _teams; }

   }

}

 

public class Team

{

   private string _name;

 

   public string Name

   {

      get { return _name; }

   }

}

Basically, we have a League, which has a collection Conferences, which in turn have a collection of Divisions and each division having the Teams belonging to that division.  Let's say the task is to go collecting the names of all teams in the league - the code to do that is simple, you just loop through the conferences, loop through the division and then loop through the teams.  The code to do so will look like the following:

List<string> teamNames = new List<string>();

League league = new League();

 

foreach (Conference conference in league.Conferences)

{

   if (conference == null)

      continue;

 

   foreach (Division division in conference.Divisions)

   {

      if (division == null)

         continue;

 

      foreach (Team team in division.Teams)

      {

         if (team == null)

            continue;

 

         teamNames.Add(team.Name);        

      }

   }

}

The code above, it works, but it just seems mundane - you have multiple foreach loops, multiple null checks - it would be nice if we can refactor this somewhat.  So, we can create a method to refactor this - basically the method needs to be able to loop through a collection, check for null, and also do something else to the item being iterated.  In the code above, it needs to basically iterate through the inner collection twice (one for division and one for team), and when iterating through teams, it needs to add the team name to the collection (not iterate through).  With that pattern, a refactored method can be written as follows:

private void ForEach<T>(IEnumerable<T> enumerable, Action<T> action )

{

   if (enumerable == null)

      return;

 

   foreach (T item in enumerable)

   {

      action(item);

   }

}

The method above will basically take an enumerable, checks to make sure it can enumerate it, and then loop through that enumerable and do something to the item being iterated - these 3 things satisfy the condition of the refactored method as mentioned above.  Now, that something that needs to be done is defined by the Action<T> delegate that the consumer of this function needs to specify.  So the above foreach loops can be written (using anonymouse delegates) as follows:

ForEach(league.Conferences, delegate(Conference conf)

{

   ForEach(conf.Divisions, delegate(Division div)

   {

      ForEach(div.Teams, delegate(Team team)

      { teamNames.Add(team.Name); });

   });

});

Well... it looks OK, but it's fairly unreadable - not to mention the developer needs to make sure everything ends properly, especially with the combination of the curly braces and the parentheses.  Enters Lamda expression, and the above code becomes like this instead:

ForEach(league.Conferences,

   conf => ForEach(conf.Divisions,

      div => ForEach(div.Teams,

         team => { if (team != null) teamNames.Add(team.Name); })));

Now, it is more compact, no question about it.  However, is it more readable?  Given the above example, assuming the developer knows what needs to be expected, it may be more readable.  6 months from now if I were walking through the code, I would imagine it would take me awhile to read through that line - it requires knowing what the ForEach method does, it requires the developer to understood what the lambda expression is doing at each level, and it will be very hard to customize if the need arises.

That said, I have to say I learned quite a bit from trying to refactor the above code into lambda expressions - it gives me a better understanding as to how to use them, how to refactor methods as lambda expressions, and also shows me a glimpse as to how they can be dangerous.  Most probably, I would make the last piece of logic (collecting the team name) to be its own function and pass that as a delegate, instead of writing the delegate in-line.  Two reasons for that:

  1. It's the portion where most probably will require logic changes (say not to add the team name if team name is empty, or has spaces, etc.)
  2. It limits the consumer code to read like calling functions, so as I read through the code, I can understand it calls another piece of code (method) and I don't have to read the function definition in-line.

The above example is also a bit advanced since we're refactoring multiple foreach loops into a recursive delegate calls - take some time to make it sink through - it took me a good 2 hours to go through this the first time around.

Posted on Monday, February 11, 2008 9:16 AM .NET | Back to top


Comments on this post: My foray into Lambda Expressions

# re: My foray into Lambda Expressions
Requesting Gravatar...
How can i return multiple values from a ForEach loop ?
Is it possible to have two sums inside a ForEach ?
for eg something like Collection.ForEach(unit => totNumber = totNumber+ unit.Number,totAmt = totAmt + unit.Amount)

Thanks !
Left by .Net NewBie on Sep 29, 2008 10:19 AM

# re: My foray into Lambda Expressions
Requesting Gravatar...
For expression-body lambda expressions, you can only provide a single statement / operation; you can't separate statements using semicolons.

You can do a statement body, which will accomplish what you wanted:
Collection.ForEach(unit => { a += unit.Number; b += unit.Number; });

The curly braces make it look weird, but think about Lambda Expressions being a neater shorthand of anonymous delegates.
Left by Muljadi Budiman on Oct 01, 2008 7:33 AM

# re: My foray into Lambda Expressions
Requesting Gravatar...
About the control thing, have you considered remapping your keyboard slightly further? I've been very happy with Ctrl, Meta, Super, [Hyper] moving outwards symmetrically from the spacebar. This has you hitting Ctrl with your thumb.And, about the M-Tab thing: Firstly, I'd recommend rebinding (I'm using F3 for some reason). Secondly, I'd point out that Escape counts as Meta - hit escape then Tab. Finally, have you tried hippie-expand? It seems to work pretty well for me (although my lisp experience is mostly common lisp, where symbol completion is C-c TAB).
Left by giochi di poker in rete on Mar 30, 2010 10:20 PM

Your comment:
 (will show your gravatar)


Copyright © Muljadi Budiman | Powered by: GeeksWithBlogs.net