Делегат и лямбда-функция. Простые примеры

Задача отбора

Объект — массив целых чисел класса AROB. Требуется выбрать из этого массива элементы, удовлетворяющие некоторому, неизвестному пока, условию. И сохранить отобранные элементы в другой объект.
Объявим в классе AROB делегат
public delegate bool EQU(int x);
В классе AROB объявим метод, осуществляющий этот отбор:
public AROB Find_All(EQU equ)
Для отбора необходимых элементов массива из объекта X в объект Y достаточно вместо equ написать нужный предикат, например:
Y = X.Find_All(x => x > 25);
где лямбда-выражение в скобках метода указывает условие отбора.
Далее все должно быть понятно из комментариев в тексте.
Программная реализация

using System;
namespace ARRAY_OBJECT
{
public class Program
{
public static void Main()
{
   AROB X = new AROB(12, 1, 50);
   X.Out("Начальный массив");
   AROB Y;
   Y = X.Find_All(x => x > 25);
      // в скобках - конкретизируем предикат от х через задание лямбда-функции:
   Y.Out("Выборка x > 25");
   AROB Z;
      // или так:
   Z = X.Find_All(x => x < 26);
   Z.Out("Выборка x < 26");
}
}
public class AROB
{
   // делегат
   public delegate bool EQU(int x);
   // поля класса AROB
   protected int n;
   protected int[] a;
   // конструктор 1
   public AROB(int N)
   {
      n = N;
      a = new int[n];
   }
   // конструктор 2
   public AROB(int N, int min, int max)
   {
      Random rnd = new Random();
      n = N;
      a = new int[n];
      for (int i = 0; i < n; i++)
         a[i] = rnd.Next(min, max + 1);
   }
   // Метод - Вывод массива
   public void Out(string info)
   {
      Console.WriteLine(info);
      foreach (int el in a)
        Console.Write(el + "\t");
      Console.WriteLine();
   }
   // Метод Изменить j-й элемент массива на E
   public void Set_Element(int j, int E)
   {
      a[j] = E;
   }
   // Метод "Получить выборку - новый массив через экземпляр делегата equ
   // Вместо edu - любое лямбда-выражение - предикат от одной целочисленной переменной
   public AROB Find_All(EQU equ)
   {
      int b = 0; // число элементов, для которых equ(x) = true
      foreach(int el in a)
      if(equ(el))
         b += 1;
      AROB Y = new AROB(b); // инициализация нового массива
      // заполнение нового массива по условию equ(x) = true
      b = 0;
      for (int i = 0; i < n; i++)
         if (equ(a[i]))
         {
            Y.Set_Element(b, a[i]);
            b++;
         }
      return Y;
   }
}
}

Встроенный делегат Action и лямбда оператора

Делегат задает формат методов void:

public delegate void Action(T obj);

Данный делегат имеет ряд перегруженных версий. Каждая версия принимает разное число параметров, может передано до 16 значений в метод.
Создадим стандартное приветствие: Привет, имя!
Решение

Action greet = name =>
{
   string greeting = $"Hello, {name}!";    
   Console.WriteLine(greeting);           
};

Вызов метода с передачей одного параметра string (может использоваться многократно, меняя имя):

greet("Alexander");

Получим: Hello, Alexander!

Встроенный делегат Predicate и лямбда выражения

используется для сопоставления некоторого объекта T определенному условию. В качестве выходного результата возвращается значение True, если условие соблюдено, и False, если нет.
Решение

Predicate isPositive = delegate (int x) { return x > 0; }; 
// где {...} - тело метода (на вход - int x, результат - bool по типу встроенного делегата)
Predicate isNegative = y => y < 0;
Console.WriteLine(isPositive(20)); // true
Console.WriteLine(isNegative(-20)); // true

В отличие от Action делегат Predicate в качестве входного значения принимает один объект. Поэтому если на вход делегата хотите подать большее количество данных, объедините их в структуру или класс.

Делегат Predicate, структура и лямбда выражения

Пусть вне класса объявлена следующая структура:

public struct XYZ
{
   public int x;
   public int y;
   public int z;
   public XYZ(int X, int Y, int Z)
   {
      x = X;
      y = Y;
      z = Z;
   }
}

И пусть нам требуется проверка условия строгой упорядоченности чисел по убыванию. Тогда:
Решение

Predicate isPositiv = P => P.x > P.y;              // лямбда-выражение    
Predicate isDownStrong = P => P.x > P.y && P.x > P.z && P.y > P.z;  //   
XYZ p = new XYZ(7,6,6); 
Console.WriteLine(isPositiv(p));     // True 
Console.WriteLine(isDownStrong(p));  // False

Func — встроенный тип делегата и лямбда выражение

Делегат Func< >() является наиболее общим встроенным делегатом. Он возвращает результат действия и может принимать параметры. Он имеет различные формы:
от Func<out T>(), где T — тип возвращаемого значения,
до Func <in T1, in T2, … , in T16, out TResult >(),
то есть может принимать до 16 параметров
Пример. Т1 = int, Tresult = bool.

Func<int, bool> equalsFive = x => x == 5; // лямбда-выражение
bool result = equalsFive(4);
Console.WriteLine(result); // False
result = equalsFive(5);
Console.WriteLine(result); // true

Пример 2. Т1 = int, T2 = int, Tresult = int.

Func<int, int, int> MultyXY = (x,y) => x * y;
int res = MultyXY(4,5);
Console.WriteLine(res); // 20
res = MultyXY(5,5);
Console.WriteLine(res); // 25

Пример 2а. Или через собственный делегат, объявленный как

delegate int DelegateType(int i, int j);

который равнозначен Func<int, int, int>

DelegateType myDelegate = (x,y) => x * y;
int square = myDelegate(5,6); // 30
Console.WriteLine(square);

Пример 3. Подстановка лямбды выражения вместо имени метода
Пусть имеется некоторый метод, который выполняет вычисления только, если x1 > 0 (иначе результат = 0).

static double GetDouble(double x1, Func<double, double> retF)
{
   double result = 0;
   if (x1 > 0)
      result = retF(x1);
   return result;
}

Тогда вызов метода, вычисляющего произвольное лямбда выражение, запишется просто:

double n2 = GetDouble(6, x => x * x); 
Console.WriteLine(n2);

Результат: 36

ПРИМЕРЫ из LINQ

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

Пусть задан некоторый массив   целых чисел. Тогда можно использовать метод Count(), в котором условие отбора задается лямбда выражением:

int[] numbers = { 1, 2, 3, 4, 5, 55, 7, 8, 9, 10 };
int pairs = numbers.Count(n => n % 2 == 0);
Console.WriteLine(pairs); // 4
pairs = numbers.Count(n => n % 2 == 1);
Console.WriteLine(pairs); // 6

Пример 2. Метод Average().
При использовании Query-синтаксиса мы запишем (массив numbers из примера 1):

var average1 = (from num in numbers where num > 6 select num).Average(); //
Console.WriteLine(average1); //  17,8

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

double average2 = numbers.Where(num => num < 5).Average();
Console.WriteLine(average); // 2,5

Пример 3 Использование метода Sum():

int summ = numbers.Where(num => num < 5).Sum(); 
Console.WriteLine(summ); // 10

Проверьте эту же конструкцию для методов Max, Min, LongCount.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Пролистать наверх