views:

484

answers:

3

I have recently moved to .net 3.0 (windows forms, C#). I want to know more about predicates and lambda expressions. Where should we use them? Do they improve performance? and how do they work internally. Thanks.

+1  A: 

If you want to study the different versions of C# and how they different .My suggestion is read the book C.Sharp.in.Depth by jon skeet . This will give you the better understanding of new versions

anishmarokey
+4  A: 

If you search Stack Overflow you'll find about a thousand answers explaining what they're for. In short - a lambda is a way of writing an anonymous method at the point where you want to pass it to another method. Technically the same as the delegate syntax for an anonymous method, although with added powers of type inference so you don't need to state the parameter types. A predicate is a method that accepts some value and returns a bool - an example would be the argument to Where.

A lambda that doesn't refer to any external variables gets turned into a private static method with a made-up name. If it refers to instance members of the enclosing class, it becomes an instance method. If it refers to local variables, those variables get "hoisted" into being fields of a compiler-generated class that is allocated when the enclosing method starts running, and the lambda's body becomes a method in that new class.

As for performance, they don't make that much difference. They involve the creation of temporary objects, but I find that these are collected extremely efficiently by the GC.

Daniel Earwicker
A: 

Do they improve performance? and how do they work internally. Thanks.

For the most part, you'll never notice the performance hit. However, there are some pathological cases which will kill performance, namely overzealous use of fixed point combinators.

Its a well-known trick that we can use the Y-combinator to write recursive lambda functions, however consider the following code:

using System;
using System.Diagnostics;

namespace YCombinator
{
    class Program
    {
        static Func<T, U> y<T, U>(Func<Func<T, U>, Func<T, U>> f)
        {
            return f(x => y<T, U>(f)(x));
        }

        static int fibIter(int n)
        {
            int fib0 = 0, fib1 = 1;
            for (int i = 1; i <= n; i++)
            {
                int tmp = fib0;
                fib0 = fib1;
                fib1 = tmp + fib1;
            }
            return fib0;
        }

        static Func<int, int> fibCombinator()
        {
            return y<int, int>(f => n =>
            {
                switch (n)
                {
                    case 0: return 0;
                    case 1: return 1;
                    default: return f(n - 1) + f(n - 2);
                }
            });
        }

        static int fibRecursive(int n)
        {
            switch (n)
            {
                case 0: return 0;
                case 1: return 1;
                default: return fibRecursive(n - 1) + fibRecursive(n - 2);
            }
        }

        static void Benchmark(string msg, int iterations, Func<int, int> f)
        {
            int[] testCases = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20 };
            Stopwatch watch = Stopwatch.StartNew();
            for (int i = 0; i <= iterations; i++)
            {
                foreach (int n in testCases)
                {
                    f(n);
                }
            }
            watch.Stop();
            Console.WriteLine("{0}: {1}", msg, watch.Elapsed.TotalMilliseconds);
        }

        static void Main(string[] args)
        {
            int iterations = 10000;
            Benchmark("fibIter", iterations, fibIter);
            Benchmark("fibCombinator", iterations, fibCombinator());
            Benchmark("fibRecursive", iterations, fibRecursive);
            Console.ReadKey(true);
        }
    }
}

This program prints out:

fibIter: 14.8074
fibCombinator: 61775.1485
fibRecursive: 2591.2444

fibCombinator and fibRecursive are functionally equivalent and have the same computational complexity, but fibCombinator is a full 4100x slower due to all of the intermediate object allocations.

Juliet
It should perhaps be remembered that no C# programmer (or indeed, any user of a post-Algol language) would ever use the Y-combinator to achieve recursion, unless they wanted to teach themselves about the history of languages lacking a built-in concept of a named function that can refer to itself.
Daniel Earwicker
@Earwicker: good point :) You'd never write code like this in C#, but combinators and point-free style are all the rage in F#, OCaml, and Haskell. I've experimented with combinators and point-free style in F#, and in almost all cases the combinator version slows performance to a crawl.
Juliet