Лямбда для чайников.... кто угодно? думаю, нет
В моем стремлении понять очень странный внешний вид оператора "= > " я нашел хорошее место для начала, а автор очень кратким и понятным:
parameters => expression
Есть ли у кого-нибудь советы по пониманию основ лямбда, чтобы облегчить "расшифровку" более сложных заявлений лямбда?
Например: если мне дано что-то вроде (из ответа который я получил здесь):
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
Как я могу разбить это на более простые части?
UPDATE: хотел показать мое первое выражение лямбды. Не смейтесь надо мной, но я сделал это, не копируя пример... и он работал в первый раз:
public ModuleData[] GetStartModules( )
{ return modules.FindAll(start => start.IsBatch == true).ToArray(); }
Ответы
Ответ 1
Разрежьте образец кода:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
Итак, мы начинаем с string[]
, называемого filenames
. Мы вызываем метод расширения SelectMany
в массиве, а затем вызываем ToList
по результату:
filenames.SelectMany(
...
).ToList();
SelectMany
принимает делегат как параметр, в этом случае делегат должен принимать один параметр типа string
в качестве входных данных и возвращать IEnumerable<T>
(где выведен тип T
). Вот где лямбды выходят на сцену:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
).ToList()
Что будет здесь, так это то, что для каждого элемента массива filenames
будет вызван делегат. f
- это входной параметр, и все, что попадает справа от =>
, является телом метода, на который ссылается делегат. В этом случае Assembly.LoadFrom
будет вызываться для имени файла в массиве, передав ему имя файла в метод LoadFrom
, используя аргумент f
. В возвращаемом AssemblyInstance
будет вызван GetCustomAttributes(typeof(PluginClassAttribute), true)
, который возвращает массив экземпляров Attribute
. Поэтому компилятор не может сделать вывод, что тип T
, упомянутый ранее, Assembly
.
В возвращаемом IEnumerable<Attribute>
будет вызван Cast<PluginClassAttribute>()
, возвращая IEnumerable<PluginClassAttribute>
.
Итак, теперь мы имеем IEnumerable<PluginClassAttribute>
, и мы вызываем на нем Select
. Метод Select
похож на SelectMany
, но возвращает один экземпляр типа T
(который выводится компилятором) вместо IEnumerable<T>
. Настройка идентична; для каждого элемента в IEnumerable<PluginClassAttribute>
он вызовет определенный делегат, передав в него текущее значение элемента:
.Select(a => a.PluginType)
Опять же, a
- это входной параметр, a.PluginType
- тело метода. Итак, для каждого экземпляра PluginClassAttribute
в списке он вернет значение свойства PluginType
(я предполагаю, что это свойство имеет тип Type
).
Резюме
Если мы склеим эти кусочки и кусочки вместе:
// 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();
Лямбдас против делегатов
Позвольте закончить это, сравнив лямбда с делегатами. Возьмите следующий список:
List<string> strings = new List<string> { "one", "two", "three" };
Скажем, мы хотим отфильтровать те, которые начинаются с буквы "t":
var result = strings.Where(s => s.StartsWith("t"));
Это наиболее распространенный подход; настройте его с помощью лямбда-выражения. Но есть альтернативы:
Func<string,bool> func = delegate(string s) { return s.StartsWith("t");};
result = strings.Where(func);
Это по существу то же самое: сначала мы создаем делегат типа Func<string, bool>
(это означает, что он принимает string
как входной параметр и возвращает bool
). Затем мы передаем этот делегат в качестве параметра методу Where
. Это то, что компилятор сделал для нас за кулисами в первом примере (strings.Where(s => s.StartsWith("t"));
).
Один третий вариант - просто передать делегат не анонимному методу:
private bool StringsStartingWithT(string s)
{
return s.StartsWith("t");
}
// somewhere else in the code:
result = strings.Where(StringsStartingWithT);
Итак, в случае, когда мы смотрим здесь, выражение лямбда является довольно компактным способом определения делегата, обычно ссылающегося на анонимный метод.
И если бы у вас была энергия, прочитанная здесь, ну, спасибо за ваше время:)
Ответ 2
Итак, чтобы начать с страшного определения - лямбда - это еще один способ определения анонимного метода. Там (поскольку С# 2.0, я считаю,) был способом построения анонимных методов - однако этот синтаксис был очень... неудобным.
Итак, что такое анонимный метод? Это способ определения метода inline, без имени - следовательно, анонимного. Это полезно, если у вас есть метод, который принимает делегат, поскольку вы можете передать это lambda expression/anonymous method в качестве параметра, учитывая, что типы совпадают. Возьмите IEnumerable.Select в качестве примера, он определяется следующим образом:
IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
Если вы должны использовать этот метод, как правило, в списке "Список" и дважды выбирать каждый элемент (который является конкатенированным для себя):
string MyConcat(string str){
return str + str;
}
...
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select(MyConcat);
}
Это очень неудобный способ сделать это, особенно если вы хотите выполнить многие из этих операций - также обычно такие методы, которые вы используете только один раз. Определение, которое вы показали (parameters = > expression), очень кончилось и попадает на него. Интересно, что вам не нужно указывать тип параметров - , если они могут быть выведены из выражения. То есть. в случае выбора - мы знаем, что первый параметр должен быть типа TSource - потому что так определяется определение метода. Более того, если мы вызываем метод так же, как foo.Select(...), то возвращаемое значение выражения определит TResult.
Интересная вещь также заключается в том, что для однозадачной лямбда ключевое слово return не требуется - лямбда вернет то, что оценивает одно выражение. Однако, если вы используете блок (завернутый в '{' и '}'), вы должны включить ключевое слово return, как обычно.
Если кто-то пожелает, все равно 100% законно определять типы параметров. Используя это новое знание, попробуйте переписать предыдущий пример:
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select(s => s + s);
}
Или, с явными параметрами, указанными
void myMethod(){
IEnumerable<string> result = someIEnumerable.Select((string s) => s + s);
}
Еще одна интересная особенность лямбда в С# заключается в их использовании для построения деревьев выражений . Это, вероятно, не "новичок" , хотя, но, короче говоря, дерево выражений содержит все метаданные о лямбда, а не исполняемый код.
Ответ 3
Хорошо, вы можете увидеть лямбда как быстрый способ написать метод, который вы хотите использовать только один раз. Например, следующий метод
private int sum5(int n)
{
return n + 5;
}
эквивалентно лямбда: (n) => n + 5
. За исключением того, что вы можете вызвать метод в любом месте вашего класса, а лямбда живет только в области, которую она объявлена (вы также можете хранить лямбда в объектах Action и Func)
Другая вещь, которую может сделать для вас лямбда, - это захват области, создание закрытия. Например, если у вас есть что-то вроде этого:
...methodbody
int acc = 5;
Func<int> addAcc = (n) => n + acc;
У вас есть функция, которая принимает аргумент как прежде, но добавленная сумма берется из значения переменной. Лямбда может жить даже после того, как была определена область, в которой был определен acc.
Вы можете строить очень аккуратные вещи с лямбдами, но вы должны быть осторожны, потому что иногда вы теряете удобочитаемость с помощью трюков, подобных этому.
Ответ 4
Как говорили другие, выражение лямбда является обозначением для функции. Он связывает свободные переменные в правой части выражения с параметрами слева.
a => a + 1
создает функцию, которая связывает свободную переменную a в выражении (a + 1) с первым параметром функции и возвращает эту функцию.
Один случай, когда Lambdas чрезвычайно полезен, - это когда вы используете их для работы со структурами списков. Класс System.Linq.Enumerable предоставляет множество полезных функций, которые позволяют работать с выражениями Lambda и объектами, реализующими IEnumerable. Например, Enumerable.Where может использоваться для фильтрации списка:
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);
}
Выход будет "яблоко, манго, виноград".
Попытайтесь понять, что происходит здесь: выражение fruit = > fruit.Length < 6 создает функцию, которая возвращает true, если свойство параметра Length меньше 6.
Enumerable.Where пересекает список и создает новый List, который содержит только те элементы, для которых предоставленная функция возвращает true. Это позволяет вам писать код, который выполняет итерации над списком, проверяет предикат для каждого элемента и что-то делает.
Ответ 5
Одно хорошее простое объяснение, направленное на разработчиков, которые владеют кодированием, но не с lambdas, - это простое видео на TekPub
TekPub - Понятия: # 2 Lambdas
У вас, очевидно, много отзывов, но это еще один хороший источник и простое объяснение.
Ответ 6
Я знаю, что это немного старо, но я пришел сюда, пытаясь понять все эти вещи лямбды.
К тому моменту, когда я закончил заполнять все ответы и комментарии, у меня было более полное недоумение лямбда и я подумал, что должен просто добавить этот простой ответ (от перспективы ученика к ученикам):
Не путайте a => a + 1
как значение, добавьте 1 к a и верните результат в a. (это, скорее всего, источник путаницы для новичков.
Вместо этого сделайте следующее: a - входной параметр в функцию (неназванная функция), a + 1 - это оператор в функции (неназванная функция построена "на лету" ).
Надеюсь, что это поможет:)
Ответ 7
Лэмбда-исчисление распространено во многих языках программирования. Их также называют анонимными функциями на некоторых языках. Хотя разные языки имеют различный синтаксис для лямбда, принцип один и тот же, и их различные части обычно идентичны.
Возможно, самым известным является анонимные функции Javascript.
lol = function() {laugh()}
# is equivalent to
function lol() {laugh()}
Какая разница? Ну, иногда вы не хотите решать проблему создания функции, чтобы передать ее где-то один раз, а затем никогда больше.
window.onload = function() {laugh()}
# is way easier than
function lol() {laugh()}
window.onload = lol
Вы можете увидеть статью wikipedia для неточной информации или вы можете перейти непосредственно к Lambda в программировании в той же статье.
Ответ 8
Это только обозначение С# для записи значения функции. Это не требует предоставления функции имени, поэтому это значение иногда называют анонимной функцией. Другие языки имеют другие обозначения, но они всегда содержат список параметров и тело.
Оригинальная нотация, придуманная Alonzo Church для Лэмбда-исчисление в 1930-е годы использовали символ греческого символа в выражении λx.t
для представления функции, отсюда и название.
Ответ 9
Мой совет для понимания основ лямбда - это два раза.
Во-первых, я рекомендую изучить функциональное программирование. Haskell - хороший язык для начала в этом отношении. Книга, которую я использую и получаю много, - это Программирование в Haskell от Грэма Хаттона. Это дает хорошее обоснование в Haskell и включает объяснения лямбда.
Оттуда я думаю, вам следует рассмотреть Эрик Мейер лекции по функциональному программированию, поскольку они дают отличное введение в функциональное программирование, а также используя Haskell, и перейдя на С#.
Как только вы приняли во внимание все, что вам должно быть хорошо на пути к пониманию лямбда.
Ответ 10
В CodeProject недавно была хорошая вводная статья: делегаты С#, анонимные методы, и лямбда-выражения - O My!