views:

367

answers:

4

So I'm trying to learn more about lambda expressions. I read this question on stackoverflow, concurred with the chosen answer, and have attempted to implement the algorithm using a console app in C# using a simple LINQ expression.

My question is: how do I translate the "var result" of the lambda expression into a usable object that I can then print the result?

I would also appreciate an in-depth explanation of what is happening when I declare the outer => outer.Value.Frequency

(I've read numerous explanations of lambda expressions but additional clarification would help)

C#
//Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}

//Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}

//The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.

//If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.

      List<int> input = new List<int>();
      input.Add(5);
      input.Add(13);
      input.Add(6);
      input.Add(5);
      input.Add(13);
      input.Add(7);
      input.Add(8);
      input.Add(6);
      input.Add(5);      

      Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();

      foreach (int number in input)
      {
        if (!dictionary.ContainsKey(number))
        {
          dictionary.Add(number, new FrequencyAndValue(1, number) );
        }
        else
        {
          dictionary[number].Frequency++;
        }
      }

      var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

      // How to translate the result into something I can print?? 

For the answer complete with print commands, see my answer here.

+26  A: 

how do I translate the "var result" of the lambda expression into a usable object that I can then print the result?

First off, the "lambda expression" is only the portion of the expression that is of the form a=>b. The rest of your query is just a method call that takes a lambda as its argument.

Anyway, if I could teach people one thing about LINQ it would be this: "result" isn't the results of the query, it is the query itself.

If you want to see the results, ask the query for each result:

foreach(var item in result)
    Console.WriteLine(item.ToString());

I would also appreciate an in-depth explanation of what is happening when I declare the outer => outer.Value.Frequency

Sure. We begin by working out the types of everything involved. We see that the lambda is a function which takes a KeyValuePair and returns an int, so we generate a method

static private int MyLambda(KeyValuePair<int, FrequencyAndValue> outer)
{
    return outer.Value.Frequency;
}

Next we take that method and create a delegate out of it:

var result = dictionary.OrderByDescending(
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

and rewrite the extension method call:

var result = Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
    dictionary,
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

and rewrite the var:

IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> result =
    Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
    dictionary,
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

I hope you agree that the code you typed in is a whole lot more readable than this mess. Type inference rocks.

The result is an object which represents the ability to sort this dictionary by the given key. Read that carefully: it represents the ability to sort the dictionary by that key. It does not actually do that until you ask for a result; so far, all it is is an object that says "when asked for a result, sort the dictionary by this key".

Suppose you ask for a result. How does it compute the sorted list? It asks the dictionary for each element. Then it calls MyLambda on each element, which gives back an integer, so we now have a pair of dictionary key-value pairs and integers. It then builds a list of pairs sorted on that integer. Then it hands out elements of that list one at a time, as you ask for them.

We see that the lambda is a function which takes a KeyValuePair and returns an int" - How did you determine that? I don't see it from the method return value, nor documented in the OrderByDescending().

Ah, I see the confusion; for pedagogic reasons I fibbed a bit above regarding the exact order in which the semantic analysis proceeds.

How we do this type inference is one of the more subtle and interesting parts of C#.

Here's how it works.

We see that OrderByDescending is declared as:

static IOrderedEnumerable<T> OrderByDescending<T, K>(
    this IEnumerable<T> sequence, 
    Func<T, K> keyExtractor)

and we see we have a potential call to this method:

OrderByDescending(dictionary, o=>o.Value.Frequency)

But we do not know what T and K are. So we start by looking at everything that is NOT a lambda. Your dictionary implements IEnumerable<KeyValuePair<int, FrequencyOrValue>> so we start by saying "T is probably KeyValuePair<int, FrequencyOrValue>".

At this point there is nothing else we can deduce from stuff that is not lambdas so we start looking at the lambdas. We see that we have a lambda o=>o.Value.Frequency and so far we have determined that the type of keyExtractor is Func<KeyValuePair<int, FrequencyOrValue>, K> and we are still looking for K. So we say suppose the lambda actually was:

(KeyValuePair<int, FrequencyOrValue> o)=>{return o.Value.Frequency;}

And we ask does it bind? YES! Yes it does. We can successfully compile this lambda without error and when we do so, we see that all of its return statements return an int.

Therefore we deduce that K is int, and we now have a full type analysis of the whole thing.

This is a fairly straightforward inference; they can get much weirder. See the "type inference" archive on my blog if this subject particularly interests you.

http://blogs.msdn.com/ericlippert/archive/tags/Type+Inference/default.aspx

In particular, here's a video of me explaining the stuff above plus a few other interesting cases:

http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx

Eric Lippert
+1, +1, +1. LINQ creates queries. Some functions iterate those queries, but until you use them to iterate the query, it's *just a query*.
Randolpho
@Randolpho - How'd you vote 3 times? I tried and couldn't!, cheater.
Nick Craver
Heh. Ve haf vays of makink the upfotes.
Randolpho
"or do you want more depth than that?"A little disassembled CIL would be nice.
Anthony Pegram
@Eric - The description helps a great deal. One question is still in my mind - you mention "We see that the lambda is a function which takes a KeyValuePair and returns an int" - How did you determine that? I don't see it from the method return value, nor documented in the OrderByDescending() . Everything else is quite helpful.
CrimsonX
@Eric - think I understand what you mean - the lambda itself is "outer => outer.Value.Frequency" which has input "outer" which is a KeyValuePair and the output of "outer.Value.Frequency" which is an int. Correct?
CrimsonX
@Crimson, a dictionary contains a KeyValuePair<TKey, TValue> collection. In your example, the TKey is of type int and TValue is of type FrequencyAndValue. So if your dictionary is going to use the OrderByDescending extension method, the input is going to be of the type of the elements the dictionary contains (KeyValuePair<int, FrequencyAndValue>). You're specifying the return of the lambda to be outer.Value.Frequency, which is an integer. That's why the "lambda is a function which takes a KeyValuePair and returns an int." ... edit: yes, on your last comment.
Anthony Pegram
@Crimson: I've updated the text to address your question.
Eric Lippert
+2  A: 

The OrderByDescending Function will return a IEnumerable, actually a IOrderedEnumerable where the TSource is the type source of the original enumerable.

As you are working with a Dictionary, the OrderByDescending will return a:

 IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>>

object, which will be ordered according to the expression provided.

Jhonny D. Cano -Leftware-
+1  A: 
var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

This line is giving you an IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> called result. As for the lambda, it is of the type

Func<KeyValuePair<int,FrequencyAndValue>, int>

Which means it accepts a KeyValuePair<int, FrequencyAndValue> parameter (which you are calling outer), and returns an integer corresponding to the pair's value's Frequency property. So the resulting IOrderedEnumerable is being sorted by the frequency in a reverse order.

Anthony Pegram
A: 

For the sake of full answer documentation, I printed the output using the more general "item" as well as the more specific IOrderedEnumerable.

C#
static void Main(string[] args)
    {

      //Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}

      //Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}

      //The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.

      //If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.

      List<int> input = new List<int>();
      input.Add(5);
      input.Add(13);
      input.Add(6);
      input.Add(5);
      input.Add(13);
      input.Add(7);
      input.Add(8);
      input.Add(6);
      input.Add(5);      

      Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();

      foreach (int number in input)
      {
        if (!dictionary.ContainsKey(number))
        {
          dictionary.Add(number, new FrequencyAndValue(1, number) );
        }
        else
        {
          dictionary[number].Frequency++;
        }
      }

      var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

      // BEGIN Priting results with the help of stackoverflow answers 
      Console.Write("With Items: ");    
      foreach (var item in result)
      {
        for (int i = 0; i < item.Value.Frequency; i++)
        {
          Console.Write(item.Value.Value + " ");
        }
        //Console.WriteLine(item.Value.Frequency + " " + item.Value.Value);
      }
      Console.WriteLine();

      Console.Write("With IOrderedEnumerable: ");    
      IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> myres = result;
      foreach (KeyValuePair<int, FrequencyAndValue> fv in myres)
      {
        for(int i = 0; i < fv.Value.Frequency; i++ )
        {
          Console.Write(fv.Value.Value + " ");
        }
      }
      Console.WriteLine();
      // END Priting results with the help of stackoverflow answers 
      Console.ReadLine();
    }
    class FrequencyAndValue
    {
      public int Frequency{ get; set;}
      public int Value{ get; set;}
      public FrequencyAndValue(int myFreq, int myValue)
      {
        Value = myValue;
        Frequency = myFreq;
      }
    }
}
CrimsonX