# lambdas

Лямбдас это библиотека для реализации функциональных интерфейсов в ваших библиотеках

## How to

### Синтаксис лямбда выражения:

Поддерживаются следующие варианты синтаксиса лямбда выражений:

```bsl
// 1
"(ПараметрПервый, ПараметрВторой) -> { Возврат ПараметрПервый + ПараметрВторой }"

// 2
"(ПараметрПервый, ПараметрВторой) ->  Возврат ПараметрПервый + ПараметрВторой"

// 3
"ПараметрПервый, ПараметрВторой -> { Возврат ПараметрПервый + ПараметрВторой }"

// 4
"ПараметрПервый, ПараметрВторой -> Возврат ПараметрПервый + ПараметрВторой"

```

### Простой пример

Допустим, у вас есть библиотека, в рамках которой вы хотите позволить потребителю удалять элементы из массива по некоторому условию.

Вы добавляете метод:

```bsl
// МояКласснаяБиблиотека.os

Процедура УдалитьПоУсловию(Условие) Экспорт

    Действие = Лямбда.Выражение(Условие)
        .ВДействие();

    Для каждого Элемент из Коллекция Цикл

        Если Действие.Выполнить() Тогда
            Коллекция.Удалить(Элемент);
        КонецЕсли;

    КонецЦикла;

КонецПроцедуры
```

В данном примере вы получаете [Действие](https://oscript.io/syntax/page/Действие) на метод, который был создан по переданному выражению, и вызов вашего метода теперь может выглядеть вот так:

```bsl
// ПотребительМоейКласснойБиблиотеки.os

Процедура Удалить()

    // Удалим из коллекции все элементы которые больше или равны двум
    МояКласснаяБиблиотека.УдалитьПоУсловию("Элемент -> Возврат Элемент >= 2");

КонецПроцедуры
```

### Интерфейсы

Для лямбда выражения можно указать функциональный интерфейс который она будет реализовывать, эта возможность преследует две цели
1. Проверить что переданное выражение соответствует вашим ожиданиям, Функция это или Процедура, и количество параметров
1. Предоставить возможность не писать возврат в однострочных лямбда выражениях вашим потребителям, если это функция

В составе библиотеки поставляет ряд функциональных интерфейсов (в модуле `ФункциональныеИнтерфейсы`):
1. УниФункция   - Функция `Применить` с одним параметром
1. БиФункция    - Функция `Применить` с двумя параметром
1. ТриФункция   - Функция `Применить` с тремя параметром
1. УниПроцедура - Процедура `Принять` с одним параметром
1. БиПроцедура  - Процедура `Принять` с двумя параметрами
1. ТриПроцедура - Процедура `Принять` с тремя параметрами

Так же можно установить произвольный `ИнтерфейсОбъекта` для создания метода по нему

Если интерфейс не был передан явно, то будет предпринята попытка автоматически подобрать один из интерфейсов из списка выше, в зависимости от количества параметров лямбда выражения и наличия возврата значения в его тексте

Например:

```bsl
// МояКласснаяБиблиотека.os

Процедура Сортировать(СравнениеЗначений) Экспорт

    Интерфейс = Новый ИнтерфейсОбъекта();
    Интерфейс.ФункцияИнтерфейса("Сравнить", 2);

    Компаратор = Лямбда.Выражение(СравнениеЗначений)
        .Интерфейс(Интерфейс)
        .ВОбъект();

    Для каждого Элемент Из Коллекция Цикл

        РезультатСравнения = Компаратор.Сравнить(Элемент, СледующийЭлемент);

        Если РезультатСравнения > 0 Тогда
            // ...
        ИначеЕсли РезультатСравнения < 0 Тогда
            // ...
        Иначе
            // ...
        КонецЕсли;

        // Алгоритм сортировки

    КонецЦикла;

КонецПроцедуры
```
Тут мы использовали метод `ВОбъект` для получения сценария, который реализует наш переданный интерфейс, и обратились по имени метода, который мы отразили в интерфейсе.  

Вызов метода:

```bsl
// ПотребительМоейКласснойБиблиотеки.os

МояКласснаяБиблиотека.Сортировать("Первый, Второй -> 
    | Если Первый > Второй Тогда
    |    Возврат 1;
    | ИначеЕсли Второй > Первый Тогда
    |    Возврат -1;
    | Иначе
    |    Возврат 0;
    | КонецЕсли;"
);
```

Пример необязательного возврата:

```bsl
// МояКласснаяБиблиотека.os

Процедура Заменить(Массив, Выражение) Экспорт

    Действие = Лямбда.Выражение(Выражение)
        .Интерфейс(ФункциональныеИнтерфейсы.УниФункция())
        .ВДействие();

    Счетчик = 0;

    Для Счетчик = 0 По Массив.ВГраница() Цикл
        Массив.Установить(Счетчик, Действие.Выполнить(Массив[Счетчик]));
    КонецЦикла;

КонецПроцедуры
```

```bsl
// ПотребительМоейКласснойБиблиотеки.os

Массив = Новый Массив();
Массив.Добавить(1);
Массив.Добавить(2);
Массив.Добавить(3);

Заменить(Массив, "Элемент -> Элемент + 1");

Сообщить(Массив[0]);
Сообщить(Массив[1]);
Сообщить(Массив[2]);
```

Результат:
```
// 2
// 3
// 4
```

#### Контекст

Лямбда-выражению можно установить `Контекст`, это позволит использовать поля контекста прямо в выражении, контекстом может выступать только структура

Например:

```bsl
// МояКласснаяБиблиотека.os

Процедура СделайДело(ЧтоСделать) Экспорт

    ДелоПервое = Новый Структура("Состояние", "НеСделано");
    ДелоВторое = Новый Структура("Состояние", "НеСделано");

    Контекст = Новый Структура(
        "ДелоПервое, ДелоВторое",
        ДелоПервое,
        ДелоВторое
    );

    Делатель = Лямбда.Выражение(ЧтоСделать)
        .Контекст(Контекст)
        .ВДействие();

    Делатель.Выполнить(1);

    Сообщить("ДелоПервое = " + ДелоПервое.Состояние);
    Сообщить("ДелоВторое = " + ДелоВторое.Состояние);

КонецПроцедуры
```

Вызов метода:

```bsl
// ПотребительМоейКласснойБиблиотеки.os

    МояКласснаяБиблиотека.СделайДело("(НомерДела) -> 
    |	Если НомерДела = 1 Тогда
    |		ДелоПервое.Состояние = ""Сделано""
    |	ИначеЕсли НомерДела = 2 Тогда
    |		ДелоВторое.Состояние = ""Сделано""
    |	КонецЕсли;"
    );

```

Результат:

```
ДелоПервое = Сделано
ДелоВторое = НеСделано
```
Как мы видим, поля контекста доступны в лямбда выражении по именам ключей структуры контекста

#### Захват объекта

В лямбда выражение можно захватить какой-то объект, чтобы в тексте лямбда выражения можно было использовать публичные и приватные поля объекта, а также публичные методы

Например:

```bsl
// МояКласснаяБиблиотека.os

Функция Единичка(Алгоритм, Объект) Экспорт

    Действие = Лямбда.Выражение(Алгоритм)
        .ЗахватитьОбъект(Объект)
        .ВДействие();

    Возврат Действие.Выполнить(1);

КонецФункции
```

Вызов метода:

```bsl
// ПотребительМоейКласснойБиблиотеки.os

Перем Двойка;
Перем Тройка Экспорт;

Функция Четверка() Экспорт
    Возврат 4;
КонецФункции

Результат = МояКласснаяБиблиотека.Единичка(
    "(Единица) -> Возврат Единица + Двойка + Тройка + Четверка()",
    ЭтотОбъект
);

// Результат = 10

```

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

## Программный интерфейс

### Лямбда

#### Выражение

```bsl
// Создаёт лямбда-выражение из переданного выражения
//
// Параметры:
//   Выражение - Строка - Выражение, из которого будет создано лямбда-выражение
//
//  Возвращаемое значение:
//   ЛямбдаВыражение - Созданное лямбда-выражение
//
Функция Выражение(Выражение) 
```
---

### ЛямбдаВыражение

#### ПриСозданииОбъекта

```bsl

Процедура ПриСозданииОбъекта(Выражение) 
```

#### Интерфейс

```bsl
// Устанавливает функциональный интерфейс для лямбда выражения.
//  В случае если метод интерфейса это функция:
//   1. Добавляет "Возврат" в лямбда выражение если оно однострочное и не содержит возврат
//   2. Проверяет что многострочное выражение содержит возврат значения
//  В случае В случае если метод интерфейса это процедура:
//   1. Проверяет что выражение не содержит возврат значения
//
// Параметры:
//   Интерфейс - ИнтерфейсОбъекта - Устанавливаемый функциональный интерфейс
//
//  Возвращаемое значение:
//   ЛямбдаВыражение - Инстанс текущего выражения
//
Функция Интерфейс(Интерфейс) 
```

#### Контекст

```bsl
// Устанавливает контекст для лямбда выражения.
// При исполнении лямбда выражения все значения контекста будут доступны для использования в лямбда выражении
//
// Параметры:
//   Контекст - Структура - Устанавливаемый контекст
//
//  Возвращаемое значение:
//   ЛямбдаВыражение - Инстанс текущего выражения
//
Функция Контекст(Контекст) 
```

#### ЗахватитьОбъект

```bsl
// Захватывает переданный объект для использования его в качестве контекста лямбда выражения.
// При исполнении лямбда выражения в нём будут доступны публичные и приватные поля переданного объекта
//  а так же публичные методы.
//
// Параметры:
//   Объект - Сценарий - Захватываемый объект
//
//  Возвращаемое значение:
//   ЛямбдаВыражение - Инстанс текущего выражения
//
Функция ЗахватитьОбъект(Объект) 
```

#### ВДействие

```bsl
// Возвращает действие (делегат) на метод созданный по лямбда выражению
//
//  Возвращаемое значение:
//   Действие - Действие на метод лямбда выражения
//
Функция ВДействие() 
```

#### ВОбъект

```bsl
// Возвращает сценарий созданный по лямбда выражению, который содержит метод согласно интерфейса
//
//  Возвращаемое значение:
//   Сценарий - Созданный сценарий
//
Функция ВОбъект() 
```
---

### ФункциональныеИнтерфейсы

#### УниФункция

```bsl
// Возвращает интерфейс объекта содержащий функцию с одним параметром
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция УниФункция() 
```

#### БиФункция

```bsl
// Возвращает интерфейс объекта содержащий функцию с двумя параметрами
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция БиФункция() 
```

#### ТриФункция

```bsl
// Возвращает интерфейс объекта содержащий функцию с тремя параметрами
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция ТриФункция() 
```

#### УниПроцедура

```bsl
// Возвращает интерфейс объекта содержащий процедуру с одним параметром
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция УниПроцедура() 
```

#### БиПроцедура

```bsl
// Возвращает интерфейс объекта содержащий процедуру с двумя параметрами
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция БиПроцедура() 
```

#### ТриПроцедура

```bsl
// Возвращает интерфейс объекта содержащий процедуру с тремя параметрами
//
//  Возвращаемое значение:
//   ИнтерфейсОбъекта - Искомый интерфейс
//
Функция ТриПроцедура() 
```

