C#. Лямбда-вирази. Загальні поняття про лямбда-вирази. Лямбда-оператор. Одиночні та блочні лямбда-вирази




Лямбда-вирази. Загальні поняття про лямбда-вирази. Лямбда-оператор. Одиночні та блочні лямбда-вирази


Зміст


Пошук на інших ресурсах:

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 утворити трикутник?

IT = (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;
};

 

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² – 2·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#. Делегат має одну сигнатуру (оголошена в типі), але в залежності від ситуації при виклику цього делегата виконується різна робота (програмний код).

Такий підхід є ефективним при написанні великих програмних систем, коли різні фрагменти коду (об’єкти) дають сигнали одному делегату про те, що йому потрібно виконати якусь роботу. Яку саме роботу має виконати делегат – визначається в залежності від ситуації та об’єкту який згенерував повідомлення. У результаті генеруються так звані події, які обробляються з допомогою делегатів. Розгляд роботи подій це вже інша (наступна) тема.

 


Зв’язані теми