Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda for Dummies....anyone, anyone? I think not

Tags:

lambda

c#-3.0

In my quest to understand the very odd looking ' => ' operator, I have found a good place to start, and the author is very concise and clear:

parameters => expression 

Does anyone have any tips on understanding the basics of lambdas so that it becomes easier to 'decipher' the more complex lambda statements?

For instance: if I am given something like (from an answer I received here):

filenames.SelectMany(f =>          Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)         .Cast<PluginClassAttribute>()         .Select(a => a.PluginType) ).ToList(); 

How can I go about breaking this down into more simple pieces?


UPDATE: wanted to show off my first lambda expression. Don't laugh at me, but I did it without copying someone's example...and it worked the first time:

public ModuleData[] GetStartModules( ) { return modules.FindAll(start => start.IsBatch == true).ToArray(); } 
like image 414
IAbstract Avatar asked Jan 30 '10 09:01

IAbstract


People also ask

How do you explain lambda?

A lambda function is a small anonymous function. A lambda function can take any number of arguments, but can only have one expression.

What is AWS lambda in simple terms?

AWS Lambda is a serverless compute service that runs your code in response to events and automatically manages the underlying compute resources for you. These events may include changes in state or an update, such as a user placing an item in a shopping cart on an ecommerce website.

How do you read lambda expressions?

All lambda expressions use the lambda operator =>, which is read as "goes to". The left side of the lambda operator specifies the input parameters (if any) and the right side hold the expression or statement block. The lambda expression x => x * 2 is read "x goes to 2 times x." This reduced the no.

What is the correct way to use a lambda function?

Syntax. Simply put, a lambda function is just like any normal python function, except that it has no name when defining it, and it is contained in one line of code. A lambda function evaluates an expression for a given argument. You give the function a value (argument) and then provide the operation (expression).


2 Answers

Let's dissect your code sample:

filenames.SelectMany(f =>          Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)         .Cast<PluginClassAttribute>()         .Select(a => a.PluginType) ).ToList(); 

So, we start off with a string[] called filenames. We invoke the SelectMany extension method on the array, and then we invoke ToList on the result:

filenames.SelectMany(    ... ).ToList(); 

SelectMany takes a delegate as parameter, in this case the delegate must take one parameter of the type string as input, and return an IEnumerable<T> (Where the type of T is inferred). This is where lambdas enter the stage:

filenames.SelectMany(f =>          Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) ).ToList() 

What will happen here is that for each element in the filenames array, the delegate will be invoked. f is the input parameter, and whatever comes to the right of => is the method body that the delegate refers to. In this case, Assembly.LoadFrom will be invoked for filename in the array, passing he filename into the LoadFrom method using the f argument. On the AssemblyInstance that is returned, GetCustomAttributes(typeof(PluginClassAttribute), true) will be invoked, which returns an array of Attribute instances. So the compiler can not infer that the type of T mentioned earlier is Assembly.

On the IEnumerable<Attribute> that is returned, Cast<PluginClassAttribute>() will be invoked, returning an IEnumerable<PluginClassAttribute>.

So now we have an IEnumerable<PluginClassAttribute>, and we invoke Select on it. The Select method is similar to SelectMany, but returns a single instance of type T (which is inferred by the compiler) instead of an IEnumerable<T>. The setup is identical; for each element in the IEnumerable<PluginClassAttribute> it will invoke the defined delegate, passing the current element value into it:

.Select(a => a.PluginType) 

Again, a is the input parameter, a.PluginType is the method body. So, for each PluginClassAttribute instance in the list, it will return the value of the PluginType property (I will assume this property is of the type Type).

Executive Summary
If we glue those bits and pieces together:

// process all strings in the filenames array filenames.SelectMany(f =>          // get all Attributes of the type PluginClassAttribute from the assembly         // with the given file name         Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)         // cast the returned instances to PluginClassAttribute         .Cast<PluginClassAttribute>()         // return the PluginType property from each PluginClassAttribute instance         .Select(a => a.PluginType) ).ToList(); 

Lambdas vs. Delegates
Let's finish this off by comparing lambdas to delegates. Take the following list:

List<string> strings = new List<string> { "one", "two", "three" }; 

Say we want to filter out those that starts with the letter "t":

var result = strings.Where(s => s.StartsWith("t")); 

This is the most common approach; set it up using a lambda expression. But there are alternatives:

Func<string,bool> func = delegate(string s) { return s.StartsWith("t");}; result = strings.Where(func); 

This is essentially the same thing: first we create a delegate of the type Func<string, bool> (that means that it takes a string as input parameter, and returns a bool). Then we pass that delegate as parameter to the Where method. This is what the compiler did for us behind the scenes in the first sample (strings.Where(s => s.StartsWith("t"));).

One third option is to simply pass a delegate to a non-anonymous method:

private bool StringsStartingWithT(string s) {     return s.StartsWith("t"); }  // somewhere else in the code: result = strings.Where(StringsStartingWithT); 

So, in the case that we are looking at here, the lambda expression is a rather compact way of defining a delegate, typically referring an anonymous method.

And if you had the energy read all the way here, well, thanks for your time :)

like image 113
Fredrik Mörk Avatar answered Sep 17 '22 20:09

Fredrik Mörk


So, to start off with the scary definition - a lambda is another way of defining an anonymous method. There has (since C# 2.0 I believe) been a way to construct anonymous methods - however that syntax was very... inconvinient.

So what is an anonymous method? It is a way of defining a method inline, with no name - hence being anonymous. This is useful if you have a method that takes a delegate, as you can pass this lambda expression / anonymous method as a parameter, given that the types match up. Take IEnumerable.Select as an example, it is defined as follows:

IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector); 

If you were to use this method normally on say a List and select each element twice (that is concatenated to itself):

string MyConcat(string str){     return str + str; }  ...  void myMethod(){     IEnumerable<string> result = someIEnumerable.Select(MyConcat); } 

This is a very inconvinient way of doing this, especially if you wish to perform many of these operations - also typically these kinds of methods you only use once. The definition you showed (parameters => expression) is very consise and hits it spot on. The interesting thing about lambda is that you do not need to express the type of the parameters - as long as they can be infered from the expression. Ie. in Select's case - we know the first parameter must be of type TSource - because the definition of the method states so. Further more - if we call the method just as foo.Select(...) then the return value of the expression will define TResult.

An interesting thing as well is that for one-statement lambda's the return keyword is not needed - the lambda will return whatever that one expression evaluates to. However if you use a block (wrapped in '{' and '}') then you must include the return keyword like usual.

If one wishes to, it is still 100% legal to define the types of the parameters. With this new knowledge, let's try and rewrite the previous example:

void myMethod(){     IEnumerable<string> result = someIEnumerable.Select(s => s + s); } 

Or, with explicit parameters stated

void myMethod(){     IEnumerable<string> result = someIEnumerable.Select((string s) => s + s); } 

Another interesting feature of lambda's in C# is their use to construct expression trees. That is probably not "beginner" material though - but in short an expression tree contains all the meta data about a lambda, rather than executable code.

like image 45
kastermester Avatar answered Sep 18 '22 20:09

kastermester