L is for… Lambda

lIf the lambda operator and associated lambda expressions are Greek to you, read on!  lambda (λ) here refers to λ-calculus, which is a formal mathematical system introduced in the 1930s.  It’s evolved to become the basis of the functional programming paradigm embodied by F# and languages such as Haskell, Scheme, and Erlang.

Elements of functional programming have been incorporated as well into the latest versions of both Visual Basic and C#, where a lambda expression is quite simply a concise way to write an anonymous method.  Those of you who are using .NET 3.5 and have adopted Language Integrated Query (LINQ) into your programming toolset are likely already quite familiar with lambda expressions.

Let’s start with a simple example, in which we have class representing the lengths of the three sides of a triangle, and we wish to find the first triangle in a list that is a right triangle.  Here’s a possible class definition in Visual Basic:

 Class Triangle
     Public ReadOnly v As List(Of Integer) = New List(Of Integer)(3)
     Sub New(ByVal s1 As Integer, ByVal s2 As Integer, ByVal s3 As Integer)
             v.Add(s1)
             v.Add(s2)
             v.Add(s3)
             v.Sort()
     End Sub
     Overrides Function ToString() As String
         ToString = String.Format("({0}, {1}, {2})", v(0), v(1), v(2))
     End Function
 End Class

And here’s a console program that makes use of this class above; we’ll walk through it line by line below.

    1:  Sub Main()
    2:      Dim list As List(Of Triangle) = New List(Of Triangle)
    3:      list.Add(New Triangle(1, 2, 4))
    4:      list.Add(New Triangle(5, 12, 13))
    5:      list.Add(New Triangle(4, 4, 4))
    6:   
    7:      Dim TriangleCheck As Predicate(Of Triangle)
    8:      TriangleCheck = New Predicate(Of Triangle)(AddressOf IsRight)
    9:      Dim t = list.Find(TriangleCheck)
   10:   
   11:      If t Is Nothing Then
   12:          Console.WriteLine("No right triangles found")
   13:      Else
   14:          Console.WriteLine("Right Triangle Found: " + t.ToString())
   15:      End If
   16:   
   17:      Console.ReadLine()
   18:  End Sub
   19:   
   20:  Function IsRight(ByVal t As Triangle) As Boolean
   21:      IsRight = t.v(0) ^ 2 + t.v(1) ^ 2 = t.v(2) ^ 2
   22:  End Function

 

  • On lines 2 –5, I’ve just defined an array of three 3-tuples that represent triangles; only the second one is a right triangle.
  • Line 7 declares a delegate reference.  Predicate(Of T) is defined in the System namespace and represent a method that accepts and object returns either true or false depending on whether the object passes the test implemented by the delegate method.
  • In Line 8, the delegate is bound to the function IsRight (defined in Lines 20-22), which applies the Pythagorean theorem to determine if the triangle is indeed a right triangle.
  • In Line 9, the delegate reference is passed into the List class’ Find method, which accepts a Predicate delegate, in this case IsRight. The result of Line 9, namely t, is an instance of the Triangle class.  Here I used type inference via the Dim keyword versus declaring a specific variable of type Triangle. In this particular case, either mechanism is fine.
  • The result of this program, if you haven’t figured it out, is the following line:

Right Triangle Found: (5, 12, 13)

 
In the spirit of full disclosure, the code above could be simplified.  Lines 7 and 8, while illustrative, are not strictly required, and I could simply have coded the following in Line 9, since both C# and Visual Basic can infer the correct delegate and create it automatically.

 Dim t = list.Find(AddressOf IsRight)

The key concept though is the fact that a named method, IsRight, was required.  In Visual Basic 9, with the introduction of lamdba expressions, I can more concisely implement this in-line as:

    1:  Sub Main()
    2:      Dim list As List(Of Triangle) = New List(Of Triangle)
    3:      list.Add(New Triangle(1, 2, 4))
    4:      list.Add(New Triangle(5, 12, 13))
    5:      list.Add(New Triangle(4, 4, 4))
    6:   
    7:      Dim t = list.Find(Function(n As Triangle) _
    8:                  (n.v(0) ^ 2 + n.v(1) ^ 2 = n.v(2) ^ 2))
    9:   
   10:      If t Is Nothing Then
   11:          Console.WriteLine("No right triangles found")
   12:      Else
   13:          Console.WriteLine("Right Triangle Found: " + t.ToString())
   14:      End If
   15:   
   16:      Console.ReadLine()
   17:  End Sub

In Line 7, the Function keyword introduces our lambda expression, and the argument to that expression (n) is automatically inferred from the context of the Find method.  So essentially, every item in the list is subjected to the expression, and only the first for which the lambda expression returns true is retained in the output object, t.

In C#, the implementation would look something like the following; note the use of the lambda operator (=>) here as well as the use of var (versus Dim in Visual Basic) for type inference.

 static void Main(string[] args)
 {
     List<Triangle> list = new List<Triangle>();
     list.Add(new Triangle(1, 2, 4));
     list.Add(new Triangle(5, 12, 13));
     list.Add(new Triangle(4, 4, 4));
  
     var t = list.Find(n => 
         Math.Pow(n.v[0], 2) + Math.Pow(n.v[1], 2) == Math.Pow(n.v[2], 2));
  
     if (t == null)
         Console.WriteLine("No right triangles found");
     else
         Console.WriteLine("Right Triangle Found: " + t.ToString());
     Console.ReadLine();
 }
  
 public class Triangle
 {
     public List<Int32> v { get; private set; }
     public Triangle(Int32 s1, Int32 s2, Int32 s3)
     {
         v = new List<Int32>(3);
         v.Add(s1); v.Add(s2); v.Add(s3);
         v.Sort();
     }
     public String ToString()
     {
         return String.Format("({0}, {1}, {2})", v[0], v[1], v[2]);
     }
 }

C# actually has a bit of an edge (for now) over Visual Basic in terms of lambda expressions.  Currently, you can only provide a single expression in Visual Basic, whereas in C# you can provide multiple statements within your lambda.  For C#, it’s pretty much just a bit of syntactic sugar on top of anonymous methods, which are not supported in Visual Basic 9.

This changes though in Visual Basic 2010, in which statement lambdas will be supported!  This is one of several awesome features planned for the next version of Visual Basic; for more information on other features, check out this blog post by the Visual Basic Team.  There’s also two excellent Channel 9 videos on Visual Basic 2010 features: