Лямбда-выражения. Общие понятия о лямбда-выражениях. Лямбда-оператор. Одиночные и блочные лямбда-выражения

Лямбда-выражения. Общие понятия о лямбда-выражениях. Лямбда-оператор. Одиночные и блочные лямбда-выражения


Содержание



1. Какие есть способы создания анонимных функций?

В языке программирования C# есть два способа создания анонимных функций:

  • анонимные методы;
  • лямбда-выражения.

Более подробно работа анонимных методов описывается в темах:

 

2. Для чего в C# используются лямбда-выражения?

Цель использования лямбда-выражений точно такая же как и анонимных методов. Лямбда-выражения являются альтернативой анонимным методам.
Лямбда-выражения позволяют программировать функции в упрощенном виде без использования имени с помощью специального оператора, который обозначается ‘=>’.

 

3. Что такое лямбда-оператор?

Лямбда-оператор обозначается ‘=>’. Дословно можно сформировать лямбда-оператор как «переходит» или «становится». Оператор ‘=>’ делит лямбда-выражение на две части.
В левой части лямбда-оператора указывается один входной параметр или несколько входных параметров. В правой части лямбда-оператора указывается лямбда выражение.

 

4. Какие существуют виды лямбда-выражений?

В языке C# есть две разновидности лямбда-выражений:

  • одиночные лямбда-выражения;
  • блочные лямбда-выражения.

 

5. Какая общая форма объявления одиночного лямбда-выражения? Примеры

Общая форма объявления одиночного лямбда-выражения, которое принимает один параметр:

параметр => выражение

где

  • параметр – параметр, который получает на входе лямбда-выражение;
  • выражение – непосредственно выражение, которое вычисляется.

Общая форма объявления одиночного лямбда-выражения, которое принимает несколько параметров:

(список_параметров) => выражение

где

  • список_параметров – два и более параметра, которые используются в лямбда-выражении.

Пример 1 одиночного лямбда-выражения, которое вычисляет значение cos(x+2):

x => Math.Cos(x + 2);

В данном примере x есть входным параметром. Данное выражение заменяет функцию приблизительно следующего вида:

double CalcCos2(double x)
{
    return Math.Cos(x + 2);
}

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

Пример 2 одиночного лямбда-выражения, которое получает 3 параметра с именами a, b, c. В данном примере определяется, можно ли из длин сторон a, b, c образовать треугольник?

(a, b, c) => ((a + b) > c) && ((a + c) > b) && ((b + c) > a);

В вышеприведенном лямбда-выражении используется правило: сумма двух любых длин сторон треугольника больше длины третьей стороны.

 

6. Какая общая форма объявления блочного лямбда-выражения? Пример

Общая форма объявления блочного лямбда выражения, получающего один параметр:

параметр => 
{
    // инструкции, выражения
    // ...
}

Общая форма объявления блочного лямбда-выражения, получающего несколько параметров:

(список_параметров) =>
{
    // инструкции, выражения
    // ...
}

Пример.

В примере объявлено лямбда-выражение, которое получает параметром целое число x. В коде лямбда-выражения делается подсчет количества цифр ‘5’ в целом числе x. Например, для числа 5854 лямбда-выражение возвращает значение 2.

x =>
{
    int t, d, k;
    t = Math.Abs(x);
    k = 0; // количество цифр '5'
    while (t > 0)
    {
        d = t % 10;
        if (d == 5) k++;
        t = t / 10;
     }
     return k;
};

Возвращение значения осуществляется оператором return.

 

7. Какие действия (шаги) нужно выполнить, чтобы в программе применить лямбда-выражение? Пример

Чтобы в программном коде применить лямбда-выражение, нужно выполнить следующую последовательность действий:

  1. Объявить тип делегата, совместный с лямбда-выражением.
  2. Объявить переменную этого типа делегата (экземпляр делегата).
  3. Присвоить переменной (экземпляру делегата) лямбда-выражение.
  4. Вызвать переменную (экземпляр делегата) в программном коде.

Пример. Пройдем все шаги последовательно для задачи из предшествующего пункта (п.6). Пусть нужно продемонстрировать работу лямбда-выражения, которое получает входным параметром целое число x и вычисляет количество цифр ‘5’ в этом числе.

1. Объявить тип делегата. Тип делегата объявляется в некотором классе.

// Объявить тип делегата
delegate int CalcNum5(int x);

Имя типа делегата CalcNum5. Делегат этого типа будет получать один входной параметр (x). Делегат этого типа возвращает значение типа int.

2. Объявить переменную этого типа делегата. Переменная объявляется в некотором программном коде. Это может быть код произвольного метода класса, код обработчика события и т.п.

CalcNum5 CN;

3. Присвоить переменной лямбда-выражение. В методе объявления переменной объявляется присваивание:

CN = x =>
{
    int t, d, k;
    t = Math.Abs(x);
    k = 0; // количество цифр '5'
    while (t > 0)
    {
        d = t % 10;
        if (d == 5) k++;
        t = t / 10;
    }
    return k;
};

Переменная может быть инициализирована лямбда-выражением сразу при ее объявлении (п.2) в соответствии с синтаксисом языка C#. В сокращенном виде это выглядит так:

CalcNum5 CN = x =>
{
    ...
};

4. Вызвать переменную. В том же методе где объявлено лямбда-выражение реализован вызов переменной. Имя CN – это имя делегата, который содержит лямбда-выражение.

// использование лямбда-выражения
int a = 615;
int res;
res = CN(a); // res = 1

 

8. Пример использования одиночного лямбда выражения, получающего один параметр

В данном примере продемонстрировано использование лямбда-выражения, в котором вычисляется функция y = sin²x.
Сначала объявляется тип делегата, который получает один параметр типа float и возвращает значение типа float. Параметр имеет название – x. В обработчике события button1_Click() продемонстрировано использование лямбда-выражения.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TrainLyambda
{
    public partial class Form1 : Form
    {
        // объявить тип делегата, который получает 1 параметр и возвращает значение
        delegate float GetSin2X(float x);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Лямбда-выражение, которое получает 1 параметр и возвращает значение
            GetSin2X y = x => (float)(Math.Sin(x) * Math.Sin(x));
 
            // Демонстрация использования лямбда-выражения
            float z;
            const float Pi = 3.1415f;
            z = y(0.0f); // z = 0
            z = y((float)(Pi / 2.0)); // z = 1
            z = y(0.7f); // z = 0.4150164

            label1.Text = z.ToString();
        }
    }
}

 

9. Пример использования одиночного лямбда-выражения, которое получает несколько параметров

В данном примере, для заданных x, y вычисляется функция z = sin x² · cos y.

Чтобы объявить лямбда-выражение, сначала нужно объявить тип делегата, который получает 2 параметра и возвращает значение. Тип параметров и значения – double.

Объявление типа делегата имеет вид:

// объявить тип делегата, который получает 2 параметра и возвращает значение
delegate double CalcZ(double x, double y);

Демонстрация использования лямбда-выражения из другого метода (например, обработчика события)

// Демонстрация использования лямбда-выражения
// Объявить переменную типа "делегат"
CalcZ Z;
 
// объявить лямбда-выражение, которое получает 2 параметра с именами x, y
Z = (x, y) => Math.Sin(x * x) - 2 * Math.Cos(y);

// использовать лямбда-выражение для расчета
double t;
t = Z(0.0, 0.0); // t = -2
t = Z(3.3, 1.8); // t = -0.540028

 

10. Пример блочного лямбда-выражения, которое получает несколько параметров

Задача

Дано три разных целых числа. Реализовать лямбда-выражение, которое находит наибольшее из этих трех чисел.

Решение

Сначала объявляется тип делегата, который получает три параметра целого типа и возвращает целое значение

// тип делегата, который получает 3 целочисленных параметра и возвращает целое значение
delegate int CalcMax(int a, int b, int c);

Затем, в другом программном коде, можно объявить лямбда-выражение и продемонстрировать его работу

// Объявить делегат
CalcMax CM;

// описать лямбда-выражение, которое получает 3 параметра x, y, z
// и возвращает максимальное значение из этих параметров
CM = (x, y, z) =>
{
    int max = x;
    if (max < y) max = y;
    if (max < z) max = z;
    return max;
};

// использовать лямбда-выражение для поиска максимума
int a = 8, b = 5, c = 10;
int Max;

Max = CM(a, b, c); // Max = 10
Max = CM(a + 8, b - 3, c + 1); // Max = 16

 

11. Можно ли в одном методе реализовать различные лямбда-выражения, которые соответствуют одному типу делегата?

Да, можно.

Пример.

Пусть объявлен тип делегата, получающий три параметра целого типа.

// объявление типа делегата
delegate int CalcABC(int a, int b, int c);

Тогда, в обработчике события или в другом методе можно написать так:

// Объявить делегат
CalcABC CM;

// 1. Описать лямбда-выражение, получающее 3 параметра x, y, z
// и возвращающее максимальное значение из этих параметров
CM = (x, y, z) =>
{
    int max = x;
    if (max < y) max = y;
    if (max < z) max = z;
    return max;
};

// использовать лямбда-выражение для поиска максимума
int a = 8, b = 5, c = 10;
int Max;

Max = CM(a, b, c); // Max = 10
Max = CM(a + 8, b - 3, c + 1); // Max = 16

label1.Text = "Максимум = " + Max.ToString();

// 2. Лямбда-выражение, вычисляющее сумму 3-х чисел
CM = (x, y, z) =>
{
    int s;
    s = z + x + y;
    return s;
};

int sum = CM(4, 3, 2); // sum = 9
label1.Text = "Сумма чисел = " + sum.ToString();

// 3. Лямбда-выражение, вычисляющее произведение из 3-х чисел
CM = (t, u, v) =>
{
    int mult = t * u * v;
    return mult;
};

int Mult = CM(5, 3, 10); // Mult = 150
label1.Text = "Произведение = " + Mult.ToString();

В вышеприведенном программном коде для одного типа делегата CalcABC реализовано 3 лямбда-выражения, которые выполняют следующие операции над входными параметрами:

  • находят максимальное значение;
  • находят сумму параметров;
  • находят произведение параметров.

Данный пример показывает преимущество применения делегатов в программах на C#. Делегат имеет одну сигнатуру (объявлена в типе), но в зависимости от ситуации при вызове этого делегата выполняется разная работа (программный код).

Такой подход есть эффективным при написании больших программных систем, где разные фрагменты кода (объекты) дают сигналы одному делегату о том, что нему нужно выполнить какую-то работу. Какую именно работу может выполнить делегат – определяется в зависимости от ситуации и объекта который сгенерировал сообщение. В результате генерируются так называемые события, которые обрабатываются с помощью делегатов. Рассмотрение работы событий это уже другая (следующая) тема.

 


Связанные темы