Share via


Visual Studio 2010: Lambdas may be theoretically based in Calculus, but they are easy and useful

Many of us just spent a decade or so with languages designed from the ground up as object oriented languages using dynamic dispatch, with syntax similar to C++.

The notion of a Lambda Expression is new. Lambdas sometimes seem a little syntactically sugary, but ultimately I enjoy the grammatical brevity of them. I reminisce about function pointers but I will show you some unique differences below.

Warning: These examples compile under Visual Studio 2010 Beta 1.

image

 

image

Lambdas are well documented, but nothing helps more than some real code snippets.

Note the code below results in j holding 25.

    1:  delegate int del(int i);
    2:  del myDelegate = x => x * x;
    3:  int j = myDelegate(5); //j = 25

The Lambda Operator => ( The new character here is “=>”)

It is the lambda operator =>, which is read as "goes to”.

Note some basic syntax in using the lambda operator. The code below illustrates a similar string version.

    1:          delegate void TestDelegate(string s);
    2:          private static void TestStringDelegate()
    3:          {
    4:              TestDelegate myDel = n => 
    5:                  { 
    6:                      string s = n + " " + "World"; 
    7:                      Console.WriteLine(s); 
    8:                  };
    9:              myDel("Hello");
   10:   
   11:          }

 

This block of code uses lambdas to iterate through an array of integers.

    1:          private static void TestDelegateArrayInts()
    2:          {
    3:              int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    4:              int oddNumbers = numbers.Count(n => n % 2 == 1);
    5:              MessageBox.Show("oddNumbers = " + oddNumbers.ToString());
    6:          }

oddNumbers

The MessageBox shows:

image

 

Numbers less than 6

firstNumbersLessThan6 gets the numbers {5, 4, 1, 3}, which makes perfect sense given the snippet below:

    1:              int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    2:   
    3:              var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);

Here is the view from the debugger:

image

 Multiple Parameters in Lambdas

 
    1:  int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
    2:   
    3:  var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index); 
imageThe variable firstSmallNumbers only holds 5 and 4. Why?They way I think about it, index is a keyword that specifies position.Position 0 is 5, so index is 0 and 5 > 0 is true, so keep the 5Position 1 is 4, so index is 1 and 4 > 1 is true, so keep the 4Position 2 is 1, so index is 2 and 1 > 2 is false, so discard the 1 and the restOnly 5 and 4 made it.

Lambdas show up in where clauses too

    1:  customers.Where(c => c.City == "London");

Only 3 rules for Lambdas – I’m telling you it’s up to the delegates parameters and return types

The general rules for lambdas are as follows:

  1. The lambda must contain the same number of parameters as the delegate type.
  2. Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.
  3. The return value of the lambda (if any) must be implicitly convertible to the delegate's return type

More sophisticated scenarios

Let’s use Lambdas to do more sophisticated searches and filters. Assume the following code:

    1:              List<string> list = new List<string>();
    2:              list.Add("AA");
    3:              list.Add("ABC");
    4:              list.Add("DEFG");
    5:              list.Add("XYZ");
    6:              Console.WriteLine("Through Anonymous method");
    7:              AnonMethod(list);
    8:              Console.WriteLine("Through Lambda expression");
    9:              LambdaExample(list);
   10:              Dictionary<string, int> varClothes = new Dictionary<string, int>();
   11:              varClothes.Add("Jeans", 20);
   12:              varClothes.Add("Shirts", 15);
   13:              varClothes.Add("Pajamas", 9);
   14:              varClothes.Add("Shoes", 9);

The next method to execute is “AnonMethod()”

    1:          static void AnonMethod(List<string> list)
    2:          {
    3:              List<string> evenNumbers = list.FindAll
    4:                     (
    5:                       delegate(string i)
    6:                       {
    7:                           return (i.Length % 2) == 0;
    8:                       }
    9:                     );
   10:              foreach (string evenNumber in evenNumbers)
   11:              {
   12:                  Console.WriteLine(evenNumber);
   13:              }
   14:          }
  
 Notice that we are about to add strings to our list:
  
image
 The code on lines 3 to 9 essentially grab offset [0] and [2], which hold “AA” and “DEFG”.
  
 The way to think is that the Lambda method, “FindAll()”, defines an anonymous delegate that loops through 
 all the strings and returns those strings where the length of the string is evenly divisible by “2.”
  
 Line 12 prints everything out.
  
image

Let’s do the same thing with the Lambda example.

Note that line 3 is the alternate syntax    
    1:          static void LambdaExample(List<string> list)
    2:          {
    3:              var evenNumbers = list.FindAll(i => (i.Length % 2) == 0);
    4:              foreach (string i in evenNumbers)
    5:              {
    6:                  Console.WriteLine(i);
    7:              }
    8:          }

Example: Using Two Parameters in a Lambda Expression

Only “Shoes” match the “FilterBy” criteria. Note that we have two parameters (name, count) in the “FilterBy” sample.  
    1:              Dictionary<string, int> varClothes = new Dictionary<string, int>();
    2:              varClothes.Add("Jeans", 20);
    3:              varClothes.Add("Shirts", 15);
    4:              varClothes.Add("Pajamas", 9);
    5:              varClothes.Add("Shoes", 9);
    6:              var ClothesListShortage = varClothes.FilterBy((string name, int count) => name == "Shoes" && count < 10);
  image

 

Example: Using a where clause on a list of integers

There are two great things I like here: (1) The “where” is intuitive and natural to read(2) This can be threaded, especially if we added some sorting capability
    1:  private void cmdMoreLambda_Click(object sender, RoutedEventArgs e)
    2:  {
    3:      int[] ages = new int[4];
    4:   
    5:      ages[0] = 4;
    6:      ages[1] = 9;
    7:      ages[2] = 5;
    8:      ages[3] = 6;
    9:   
   10:      var olderOrEqualTo6 =
   11:         ages.Where(delegate(int score)
   12:         {
   13:             return (score > 6);
   14:         }
   15:      );
   16:   
   17:  }
Here is the debugger showing us that the number “9” is the only one that makes it.imageThe code above shows you multi-line lambda support.

 

Source Code: The Entire Sample Class discussed above

 image

If you look carefully here, you can see how you might want to create  your own Lambda method. FilterBy<K,V>, where K is a dictionary of items, and V is KeyValueFilter.

     static class MyLambda
     {
  
         public static void TestHarness()
         {
  
             List<string> list = new List<string>();
             list.Add("AA");
             list.Add("ABC");
             list.Add("DEFG");
             list.Add("XYZ");
             Console.WriteLine("Through Anonymous method");
             AnonMethod(list);
             Console.WriteLine("Through Lambda expression");
             LambdaExample(list);
             Dictionary<string, int> varClothes = new Dictionary<string, int>();
             varClothes.Add("Jeans", 20);
             varClothes.Add("Shirts", 15);
             varClothes.Add("Pajamas", 9);
             varClothes.Add("Shoes", 9);
             var ClothesListShortage = varClothes.FilterBy((string name, int count) => name == "Shoes" && count < 10);
  
             // example of multiple parameters
             if (ClothesListShortage.Count > 0)
                 Console.WriteLine("We are short of shoes");
         }
  
         static void AnonMethod(List<string> list)
         {
             
             List<string> evenNumbers = list.FindAll
                    (
                      delegate(string i)
                      {
                          return (i.Length % 2) == 0;
                      }
                    );
             foreach (string evenNumber in evenNumbers)
             {
                 Console.WriteLine(evenNumber);
             }
         }
         static void LambdaExample(List<string> list)
         {
             var evenNumbers = list.FindAll(i => (i.Length % 2) == 0);
             foreach (string i in evenNumbers)
             {
                 Console.WriteLine(i);
             }
         }
         public static Dictionary<K, V> FilterBy<K, V>(this Dictionary<K, V> items, KeyValueFilter<K, V> filter)
         {
             var result = new Dictionary<K, V>();
             foreach (KeyValuePair<K, V> element in items)
             {
                 if (filter(element.Key, element.Value))
                     result.Add(element.Key, element.Value);
             }
             return result;
         }   
     }

Comments