L is for… Lambda
If 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 aPredicate
delegate, in this caseIsRight
. The result of Line 9, namelyt
, is an instance of theTriangle
class. Here I used type inference via theDim
keyword versus declaring a specific variable of typeTriangle
. 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: