Blog from Saravanan Arumugam

Let us talk about Technologies

Category Archives: Lambda Expressions

Lambda expressions


A lambda expression can be understood as an anonymous function which can contain expressions and statements. It is one of the key enhancements that C# 3.0 provided to support Linq.

Lambda expressions are useful in all the places where a delegate or expression tree is applicable. In particular, lambda expressions are very useful in the IEnumerable<T> typed objects, which carries extension methods accepting delegate parameter of type Func<T,TResult>.

A lambda expression is written in the following form

parameters => expression/{statements}

Here “=>” is read as “goes to”. Parameter can be a single parameter or group of parameters enclosed within parenthesis.

All the rules of anonymous methods are applicable to lambda expressions. Lambda expressions can be categorized as expression lambda and statement lambdas.

In this paper I am going to present different features of lambda expressions with examples.

 

Simple Lambda

To square a value, a function has to take the form fn(x) = x * x;

This can be represented as x => x * x in lambda expression.

The following C# code evaluates this function with lambda expression.

delegate long squareValue(int a);

 

static void Main(string[] args)

{

    //Simple Lambda

    squareValue squareOf = x => x * x;

    Console.WriteLine("Square of {0} is {1}", 5, squareOf(5));

    //Equivalent ananymous method

    squareOf = delegate(int x) { return x * x; };

}

Multiple parameters can be supplied by enclosing them in parenthesis. e.g (x, y)

Note: An expression can take zero parameters and can return void.

 

Lambda calling external method

A lambda can call a external function as follows. The following expression valuates the function fn(x, y) = x² + y.

delegate U expressionDelegate<T, U>(T m, U n);

 

static void Main(string[] args)

{

    //Lambda expression calling a method fn(x,y) = x² + y

    expressionDelegate<int, float> expression =

        x, y) => Square(x) + y;

 

    int a = 8; float b = 5.6f;

    Console.WriteLine("Result of {0}² + {1} = {2}",

         a, b, expression2(a, b));

} 

Square(x) is the external method.

 

Lambda vs inbuilt System.Func<T, TResult> delegate

Many standard query operations take an input parameter whose type is one of System.Func<T, TResult> family. Func<T, TResult> delegate define the number of input parameters, type of the parameters and the return type. Func<T, TResult> can take up to 16 input parameters and a non void return type.

The last type specified in the Func<T, TResult> is the type of the result or return type.

For example Func<int, float, bool> indicates that this is going to hold a delegate with int and float type input parameters and is going to return a bool as output.

Func<int, int, bool> expression = (x, y) => x == y;
Console.WriteLine(expression(2, 2));

 

Currying

Currying is a function transformation technique with which a function with multiple parameters can be transformed into a series of multiple functions each taking a single parameter.

With currying technique, (x, y) = x*x + y*y is equivalent to x => (y => x*x + y*y )

Here is the C# implementation of the curried function.

//With Currying (x, y) => x*x + y*y is equivalent to x =>(y => x*x + y*y))
Func<int, Func<int, int>> curriedExpression = x => y => x * x + y * y;
 
int m = 5, n = 7;
Console.WriteLine("Result of {0}² + {1}² = {2}",m, n, curriedExpression(m)(n));

Note the declaration of curriedExpression. The outer expression is said to take the integer as input, and the output of it is another Func<> delegate. This delegate in turn accepts an integer and outputs an integer.

The execution of expression is also a little different from the that of usual expressions. curriedExpression (m) returns a function. Input n is sent to the resulting function to complete the execution.

 

Statement Lambdas

Statement lambdas take the following form

(parameters) => {statements;}

Any c# statement can be enclosed within the braces and all the paths should end with a return statement, unless the return message is declared to be void.

Let us take the expression to find the largest among 3 integers for discussion.

With the expression lambda we can specify it as

Func<int, int, int, int> greatestNumber = 
         (x, y, z) => 
         (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
Console.WriteLine("Greatest number in the given set is {0}", 
         greatestNumber(15, 6, 11));

The same can be expressed in the statement lambda as

Func<int, int, int, int> greatestNumber = (x, y, z) =>
{
    if (x > y)
        if (x > z)
            return x;
        else
            return z;
    else
        if (y > z)
            return y;
        else
            return z;
 
};

The body of the statement lambda can have any number of statements. However it is recommended to have only a few statements in them say two or three. More than that might affect the readability of the expression.

 

Lambda inside another lambda

It is possible to have nesting in lambdas. For instance the example of finding greatest among 3 numbers can be expressed as greatestOf(x, y, z) = greaterOf(greaterOf(x, y), z)

The expression greatestOf requires another expression (greaterOf) to be executed within it.

Func<int, int, int> greaterOf = (x, y) => (x > y) ? x : y;
Func<int, int, int, int> greatestOf = (x, y, z) => greaterOf(greaterOf(x, y), z);
Console.WriteLine("Greatest number in the given set is {0}", greatestOf(7, 2, 3));

 

Variable Scope in Lambda expressions

Lambda expressions can refer to the variables outside the expression within the execution scope. The variables used within the expression would be retained even if the original variable goes out of scope or garbage collected. Here the rule is that the variables in the expression should have been assigned some value before being used.

An example to reflect lambda expression’s treatment of local variables follows.

class LambdaScope
{
    static Func<string> zeroParameterLambda = null;
 
    static void Main(string[] args)
    {
        (new MyClass()).DoSomething();
        Console.WriteLine(zeroParameterLambda());
    }
 
    class MyClass
    {
        public void DoSomething()
        {
            DateTime startTime, endTime;
            startTime = endTime = DateTime.Now;
 
            zeroParameterLambda = () =>
            {
         Console.WriteLine("Hash code of startTime from lambda expression: {0}", 
                 startTime.GetHashCode());
         return string.Format("Operation started by {0} and stopped by {1}", 
                 startTime, endTime);
            };
 
            Thread.Sleep(2000);
 
            //Modify the endTime value                
            endTime = DateTime.Now;
                            
            Console.WriteLine("Hash code of startTime from DoSomething(): {0}", 
                 startTime.GetHashCode());
        }
    }
}

In the code, you can note that the zeroParameterLambda is defined within MyClass.DoSomething() method. This expression uses the local variables startTime and endTime for its definition. But the zeroParameterLambda is executed only in the Main(). Here the values of startTime and endTime are kept safe to be used in zeroParameterLambda, even though they are out of scope.

image

I am still exploring on this topic to know more on how the variables are retained and the consideration of garbage collector on these variables. Looking forward to post more papers on this topic.

 

Lambda recursion

Recursion is the concept of a function calling itself. Recursion is useful in doing mathematical functions on series of numbers, such as factorial, sum/product of a series etc. With lambda expressions, recursion is so handy.

To make the recursion, first declare the Func<T, TResult> initializing it to null. In the next line we can define the expression which can make use of the expression name declared in the first line.

Here is an example of lambda recursion to find a factorial a number and to check if the number is prime or not.

//Factorial of n or n!
Func<int, int> fact = null;
fact = x => (x == 0) ? 1 : x * fact(x - 1);
int a = 6;
Console.WriteLine("{0}! is {1}", a, fact(a));
 
//Check prime number
Func<int, int, bool> prime = null;
prime = (x, y) => (y == 1) ? true : (x % y == 0 ? false : prime(x, y - 1));
Func<int, bool> isPrime = x => prime(x, x / 2);
int b = 2011;
Console.WriteLine("{0} is a {1}prime number", b, (isPrime(b)?"":"not "));

 

Use of Lambda expression

Lambda expressions are introduced to primarily support the Linq in C# 3.0. However lambda expressions are applicable to variety of places. Let me present a few uses of lambda expressions.

 

Use of Lambda expression in place of Predicate<T> and Action<T>

Lambda expressions can be used in the old style List.FindAll<T>/ConvertAll etc. where the parameter is of type Predicate<T> or Action<T>.

List<int> numberArray = new List<int> { 1, 4, 3, 2, 6, 7, 9, 13 };
 
Console.Write("\nOdd numbers in the array");
numberArray.ForEach(x => { if ((x & 1) == 1) Console.Write(" " + x); });
 
Console.Write("\nMultiples of 3 in the array");
numberArray.FindAll(x => x % 3 == 0).ForEach(y => Console.Write(" " + y));

 

Use of lambda expression as event handlers

Lambda expressions can be used as simple event handlers.

For instance in the following code snippet, I have used the lambda expression as a handler for Console.CancelKeyPress event.

A little background about the code snippet; following console application would start printing from 0 to 10000 sequentially. While printing, the operation can be interrupted by pressing Ctrl+C in the middle. This would terminate the current console operation. Before termination, the CancelKeyPress event would be triggered. So with the following code in place, we can see a custom message before console operation gets terminated.

Console.CancelKeyPress += 
    new ConsoleCancelEventHandler(
        (object obj, ConsoleCancelEventArgs e) => 
                 Console.WriteLine("Console is about to break...")
         );
for (int i = 0; i < 10000; i++)
     Console.WriteLine(i);

 

Use of lambda expression in IEnumerable<T> extension methods

IEnumerable<T> provides a variety of extension methods to support Linq. There are many extension methods that would accept lambda expressions in place of Func<T, TResult> delegate family.

Here is an example.

var myObject = new[] {
                   new {Name = "Saravanan", Age = 20},
                   new {Name = "Thomos", Age = 18},
                   new {Name = "George", Age = 25}
                 };
 
var filteredObject = myObject.First(obj => obj.Name.StartsWith("S"));
Console.WriteLine("Filtered Output: Name={0}, Age={1}", 
         filteredObject.Name, filteredObject.Age);

 

Use of lambda expression to do operations in a different thread

The following sample does a file operation in a different thread while the main thread continues to work on something. The main advantage of this technique is that the sub thread has full access to the local variables of main thread. It would be a real delight for those who have experienced the difficulty in passing objects between the threads.

string textToBeWritten = "Some sample text";
Thread t = new Thread(new ThreadStart(() =>
{
    using (Stream stream = File.Create(@"C:\Temp\somefile.txt"))
    {
        StreamWriter sw = new StreamWriter(stream);
        lock (textToBeWritten)
        {
            sw.WriteLine(textToBeWritten);
        }
        sw.Close();
    }
}));
t.Start();

Note that the textToBeWritten is a variable outside the scope of the sub thread, but still the sub thread has complete control over it.

Note: Care should be taken to use these variables in a thread safe way. Use of lock{} can help.

 

Summary

Lambda expression is a great value-add to .Net languages. While the lambda expression provide the flexibility and short cuts to coding, it comes with the cost of readability. So the recommendation is to limit the use of lambda expression with a clear consideration of readability and maintainability. In every other sense, lambda expression should be a favorite for developers.

 

Reference materials

Lambda Expressions (C# Programming Guide)

Anonymous Methods (C# Programming Guide)

Where to use Lambda Expression

Lambda calculus

Advertisements