Подтвердить что ты не робот

Лямбда для чайников.... кто угодно? думаю, нет

В моем стремлении понять очень странный внешний вид оператора "= > " я нашел хорошее место для начала, а автор очень кратким и понятным:

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(); }
4b9b3361

Ответ 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, и перейдя на С#.

Как только вы приняли во внимание все, что вам должно быть хорошо на пути к пониманию лямбда.