c#-3.0 - expressions - linq lambda c#
Lambda para Dummies... ¿Alguien, alguien? Yo creo que no (10)
Analicemos su muestra de código:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
Entonces, comenzamos con una string[]
llamada filenames
. Invocamos el método de extensión SelectMany
en la matriz y luego invocamos ToList
en el resultado:
filenames.SelectMany(
...
).ToList();
SelectMany
toma un delegado como parámetro, en este caso el delegado debe tomar un parámetro del tipo string
como entrada, y devolver un IEnumerable<T>
(donde se deduce el tipo de T
). Aquí es donde las lambdas entran al escenario:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
).ToList()
Lo que sucederá aquí es que para cada elemento de la matriz de filenames
, se invocará al delegado. f
es el parámetro de entrada, y lo que sea que aparezca a la derecha de =>
es el cuerpo del método al que se refiere el delegado. En este caso, Assembly.LoadFrom
se invocará para filename en la matriz, pasando el nombre de archivo al método LoadFrom
utilizando el argumento f
. En AssemblyInstance
que se devuelve, se GetCustomAttributes(typeof(PluginClassAttribute), true)
, que devuelve una matriz de instancias de Attribute
. Entonces, el compilador no puede inferir que el tipo de T
mencionado anteriormente es Assembly
.
En IEnumerable<Attribute>
que se devuelve, se Cast<PluginClassAttribute>()
y se devolverá un IEnumerable<PluginClassAttribute>
.
Entonces ahora tenemos un IEnumerable<PluginClassAttribute>
, e invocamos Select
en él. El método Select
es similar a SelectMany
, pero devuelve una única instancia de tipo T
(que se deduce del compilador) en lugar de un IEnumerable<T>
. La configuración es idéntica; para cada elemento en IEnumerable<PluginClassAttribute>
invocará al delegado definido, pasando el valor actual del elemento en él:
.Select(a => a.PluginType)
De nuevo, a
es el parámetro de entrada, a.PluginType
es el cuerpo del método. Por lo tanto, para cada instancia de PluginClassAttribute
en la lista, devolverá el valor de la propiedad PluginType
(supongo que esta propiedad es del tipo Type
).
Resumen ejecutivo
Si pegamos esas piezas y piezas juntas:
// 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. delegados
Terminemos esto comparando lambdas con delegados. Toma la siguiente lista:
List<string> strings = new List<string> { "one", "two", "three" };
Digamos que queremos filtrar aquellos que comienzan con la letra "t":
var result = strings.Where(s => s.StartsWith("t"));
Este es el enfoque más común; configúralo usando una expresión lambda. Pero hay alternativas:
Func<string,bool> func = delegate(string s) { return s.StartsWith("t");};
result = strings.Where(func);
Esto es esencialmente lo mismo: primero creamos un delegado del tipo Func<string, bool>
(eso significa que toma una string
como parámetro de entrada y devuelve un bool
). Luego pasamos ese delegado como parámetro al método Where
. Esto es lo que el compilador hizo para nosotros detrás de las escenas en la primera muestra ( strings.Where(s => s.StartsWith("t"));
).
Una tercera opción es simplemente pasar un delegado a un método no anónimo:
private bool StringsStartingWithT(string s)
{
return s.StartsWith("t");
}
// somewhere else in the code:
result = strings.Where(StringsStartingWithT);
Entonces, en el caso que estamos viendo aquí, la expresión lambda es una forma bastante compacta de definir un delegado, normalmente refiriendo un método anónimo.
Y si tuviste la energía de leer todo el camino hasta aquí, bueno, gracias por tu tiempo :)
En mi búsqueda por entender el operador ''=>'' de aspecto muy extraño, he encontrado un buen lugar para comenzar , y el autor es muy conciso y claro:
parameters => expression
¿Alguien tiene algún consejo sobre la comprensión de los conceptos básicos de lambdas para que sea más fácil ''descifrar'' las declaraciones lambda más complejas?
Por ejemplo: si me dan algo como (de una respuesta que recibí aquí ):
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
¿Cómo puedo dividir esto en partes más simples?
ACTUALIZACIÓN: quería mostrar mi primera expresión lambda. No te rías de mí, pero lo hice sin copiar el ejemplo de alguien ... y funcionó la primera vez:
public ModuleData[] GetStartModules( )
{ return modules.FindAll(start => start.IsBatch == true).ToArray(); }
Bueno, puedes ver una lambda como una forma rápida de escribir un método que solo quieres usar una vez. Por ejemplo, el siguiente método
private int sum5(int n)
{
return n + 5;
}
es equivalente a lambda: (n) => n + 5
. Excepto que puede llamar al método en cualquier lugar de su clase, y el lambda solo vive en el alcance que está declarado (también puede almacenar lambdas en objetos Action y Func)
Otra cosa que Lambdas puede hacer por ti es capturar alcance, construir un cierre. Por ejemplo, si tiene algo como esto:
...methodbody
int acc = 5;
Func<int> addAcc = (n) => n + acc;
Lo que tienes allí es una función que acepta un argumento como antes, pero la cantidad añadida se toma del valor de la variable. La lambda puede vivir incluso después de que el alcance en el que se definió acc haya finalizado.
Puedes construir cosas muy lindas con lambdas, pero debes tener cuidado, porque a veces pierdes legibilidad con trucos como este.
CodeProject tuvo un buen artículo introductorio últimamente: Delegados de C #, Métodos anónimos y Expresiones de Lambda - ¡O mi!
Como han dicho otros, una expresión lambda es una notación para una función. Vincula las variables libres en el lado derecho de la expresión a los parámetros de la izquierda.
a => a + 1
crea una función que vincula la variable libre a en la expresión (a + 1) al primer parámetro de una función y devuelve esa función.
Un caso en el que Lambdas es extremadamente útil es cuando los usa para trabajar con estructuras de listas. La clase System.Linq.Enumerable proporciona muchas funciones útiles que le permiten trabajar con expresiones Lambda y objetos que implementan IEnumerable. Por ejemplo, Enumerable.Where se puede usar para filtrar una lista:
List<string> fruits = new List<string> {
"apple", "passionfruit", "banana", "mango",
"orange", "blueberry", "grape", "strawberry" };
IEnumerable<string> shortFruits = fruits.Where(fruit => fruit.Length < 6);
foreach (string fruit in shortFruits) {
Console.WriteLine(fruit);
}
La salida será "manzana, mango, uva".
Intente comprender lo que está sucediendo aquí: la expresión fruit => fruit.Length <6 crea una función que devuelve true si la propiedad Length del parámetro es menor que 6.
Enumerable.Where recorre la Lista y crea una nueva Lista que contiene solo aquellos elementos para los cuales la función suministrada devuelve verdadero. Esto le ahorra escribir código que itera sobre la Lista, verifica un predicado para cada elemento y hace algo.
Entonces, para comenzar con la definición aterradora, una lambda es otra forma de definir un método anónimo. Ha habido (desde C # 2.0 creo) una forma de construir métodos anónimos, sin embargo esa sintaxis era muy ... inconviniente.
Entonces, ¿qué es un método anónimo? Es una forma de definir un método en línea, sin nombre, por lo tanto, es anónimo. Esto es útil si tiene un método que toma un delegado, ya que puede pasar esta expresión lambda / método anónimo como parámetro, dado que los tipos coinciden. Tome IEnumerable.Select como ejemplo, se define de la siguiente manera:
IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
Si usara este método normalmente en, por ejemplo, una Lista y seleccione cada elemento dos veces (que se concatene a sí mismo):
string MyConcat(string str){
return str + str;
}
...
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select(MyConcat);
}
Esta es una forma muy inconciente de hacerlo, especialmente si desea realizar muchas de estas operaciones; también suele ser este tipo de métodos que solo utiliza una vez. La definición que mostró (parameters => expression) es muy concisa y la ubica en el lugar correcto. Lo interesante de lambda es que no necesita expresar el tipo de parámetros, siempre que puedan derivarse de la expresión . Es decir. en el caso de Select, sabemos que el primer parámetro debe ser de tipo TSource, porque la definición del método lo establece. Además, si llamamos al método solo como foo. Seleccione (...) entonces el valor de retorno de la expresión definirá TResult.
Algo interesante también es que para una declaración, la palabra clave de retorno lambda no es necesaria: la lambda devolverá lo que evalúa esa expresión. Sin embargo, si usa un bloque (incluido en ''{'' y ''}''), debe incluir la palabra clave return como siempre.
Si uno lo desea, sigue siendo 100% legal definir los tipos de los parámetros. Con este nuevo conocimiento, intentemos reescribir el ejemplo anterior:
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select(s => s + s);
}
O, con parámetros explícitos establecidos
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select((string s) => s + s);
}
Otra característica interesante de lambda en C # es su uso para construir árboles de expresión . Sin embargo, probablemente este no sea un material "principiante", pero en resumen, un árbol de expresiones contiene todos los metadatos sobre un lambda, en lugar de un código ejecutable.
Esta es solo la notación de C # para anotar un valor de función. No requiere dar un nombre a la función, por lo tanto, este valor a veces se denomina función anónima . Otros idiomas tienen otras anotaciones, pero siempre contienen una lista de parámetros y un cuerpo.
La notación original inventada por Alonzo Church para su cálculo Lambda en la década de 1930 utilizó el carácter griego lambda en la expresión λx.t
para representar una función, de ahí el nombre.
Mi consejo para comprender los conceptos básicos de lambdas es doble.
En primer lugar, recomiendo aprender sobre programación funcional. Haskell es un buen lenguaje para empezar en ese sentido. El libro que estoy usando y sacando mucho provecho es Programación en Haskell de Graham Hutton. Esto proporciona una buena base en Haskell e incluye explicaciones de lambdas.
A partir de ahí, creo que debería ver las conferencias de Erik Meijer sobre programación funcional, ya que también proporcionan una gran introducción a la programación funcional, también usan Haskell y cruzan a C #.
Una vez que hayas asimilado todo eso, estarás en camino de comprender a las lambdas.
Sé que esto es un poco viejo, pero vine aquí tratando de dar sentido a todas estas cosas lambda. En el momento en que terminé de analizar todas las respuestas y comentarios, tuve una mejor comprensión de lambda y pensé que debería simplemente agregar esta respuesta simple (desde la perspectiva de un alumno a los estudiantes):
No confundas a => a + 1
como lo que significa agrega 1 aa y devuelve el resultado a a. (Esto probablemente sea una fuente de confusión para los principiantes, en vez de verlo así: a es el parámetro de entrada en una función (función sin nombre) y a + 1 es la declaración (es) en la función (función sin nombre construida ''en el mosca'').
Espero que esto ayude :)
Una buena explicación simple dirigida a desarrolladores que son firmes con la codificación pero no con lambdas es este simple video en TekPub
TekPub - Conceptos: # 2 Lambdas
Obviamente tienes muchos comentarios aquí, pero esta es otra buena fuente y una explicación simple.
El cálculo Lambda es común en muchos lenguajes de programación. También se llaman funciones anónimas en algunos idiomas. Aunque los diferentes idiomas tienen una sintaxis diferente para lambda, el principio es el mismo, y sus diversas partes suelen ser idénticas.
Quizás el más famoso es el de las funciones anónimas de Javascript.
lol = function() {laugh()}
# is equivalent to
function lol() {laugh()}
¿Cual es la diferencia? Bueno, a veces no quieres pasar por el problema de crear una función solo para pasarla a alguna parte una vez y nunca más.
window.onload = function() {laugh()}
# is way easier than
function lol() {laugh()}
window.onload = lol
Puede ver el artículo de la wikipedia para obtener información incorrecta o puede omitir directamente a Lambda en la programación en el mismo artículo.