Перейти к основному содержимому

Лямбда-выражения

Лямбда-выражение состоит из параметров, лямбда-операции -> и тела лямбда-выражения. Например:

Пример с одним параметром
Операнд -> Операнд + 1
Пример с двумя параметром
(ПервыйПараметр, ВторойПараметр) -> ПервыйПараметр * ВторойПараметр

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

// Переменная функционального типа (Число) -> Число со значением x -> x * x
пер Квадрат: (Число)->Число = x -> x * x

При объявлении и вызове метода:

// Параметр ФТ — параметр функционального типа
метод Сравнить(А: Число, Б: Число, ФТ: (Число, Число)->Булево): Булево
возврат ФТ(А, Б)
;

метод Вызвать()
// 3-й аргумент — значение функционального типа (Число, Число) -> Булево
пер Равенство = Сравнить(2, 3, (x, y) -> x == y) // Ложь
пер Больше = Сравнить(2, 3, (x, y) -> x > y) // Ложь
пер Меньше = Сравнить(2, 3, (x, y) -> x < y) // Истина
;

Лямбда-выражение — анонимный метод, то есть для него не требуется указывать имя, и он не привязан к идентификатору. Анонимность и компактность лямбда-выражений позволяют записывать весь метод целиком (и параметры, и результат) непосредственно в список параметров другого метода или в тело другого метода как возвращаемое значение.

Синтаксис

Краткая форма

  • Без параметров:

    () -> выражение
  • Один параметр:

    (параметр) -> выражение
  • Один параметр с явным указанием типа:

    (параметр: тип) -> выражение
  • Несколько параметров:

    (параметр-1, ..., параметр-N) -> выражение
  • Несколько параметров с явным указанием типа:

    (параметр-1: тип-1, ..., параметр-N: тип-N) -> выражение

Примеры:

Без параметров
метод УзнатьДату(): Строка
пер Сегодня: ()->Дата = () -> Дата.Сейчас()
возврат Сегодня().ВСтроку() // Вернет строковое представление сегодняшнего дня
;
Один параметр
метод ВозвестиВКвадрат(): Число
пер Квадрат: (Число)->Число = А -> А * А
возврат Квадрат(5) // 25
;
Один параметр с явным указанием типа
метод ФильтроватьМассивЧисел(): Массив<Число>
знч ФильтрПоложительных = (А: Число) -> А > 0
возврат [1, -2, 3, -4].Фильтровать(ФильтрПоложительных) // [1, 3]
;
Два параметра
метод Умножить(): Число
пер УмножитьДваЧисла: (Число, Число)->Число = (А, Б) -> А * Б
возврат УмножитьДваЧисла(4, 6) // 24
;
Два параметра с явным указанием типа
метод Суммировать(): Число
знч Сумматор = (А: Число, Б: Число) -> А + Б
возврат Сумматор(7, 3) // 10
;
примечание

При использовании лямбда-выражений компилятору обязательно требуется знать типы всех параметров. Если типы параметров не указаны явно и не могут быть выведены из контекста, возникнет ошибка компиляции.

Полная форма

Для задания полной формы используется ключевое слово метод. Тело лямбда-выражения аналогично телу метода может быть многострочным и заканчивается символом ;.

  • Без параметров:

    метод() -> тело лямбда-выражения;
  • Один параметр:

    метод(параметр) -> тело лямбда-выражения;
  • Несколько параметров:

    метод(параметр-1, ..., параметр-N) -> тело лямбда-выражения;
  • Несколько параметров с типом:

    метод(параметр-1: тип-1, ..., параметр-N: тип-N) -> тело лямбда-выражения;

Примеры:

Без параметров
метод УзнатьДату(): Строка
пер Сегодня = метод() -> возврат Дата.Сейчас();
возврат Сегодня().ВСтроку() // Вернет строковое представление сегодняшнего дня
;
Один параметр
метод ВозвестиВКвадрат(): Число
пер Квадрат: (Число)->Число = метод(А) -> возврат А * А;
возврат Квадрат(5) // 25
;
Один параметр с явным указанием типа
метод ФильтроватьМассивЧисел(): Массив<Число>
знч ФильтрПоложительных = метод(А: Число)->возврат А > 0;
возврат [1, -2, 3, -4].Фильтровать(ФильтрПоложительных) // [1, 3]
;
Два параметра
метод Умножить(): Число
пер УмножитьДваЧисла: (Число, Число)->Число = метод(А, Б) -> возврат А * Б;
возврат УмножитьДваЧисла(4, 6) // 24
;
Два параметра с явным указанием типа
метод Суммировать(): Число
знч Сумматор = метод(А: Число, Б: Число) -> возврат А + Б;
возврат Сумматор(7, 3) // 10
;

Захват контекста

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

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

  • Запрещается использовать в параметрах лямбда-выражения переменные, объявленные вне тела лямбда-выражения (переменные Сообщение и Имя определены раньше):

    метод Тест(Имя: Строка): Строка
    пер Сообщение = "Привет, "
    пер Печать = (Сообщение: Строка, Имя: Строка) -> Сообщение + Имя + "!"
    возврат Печать()
    ;
  • Запрещается объявлять или изменять в теле лямбда-выражения переменные, объявленные вне тела лямбда-выражения (переменная Сообщение определена раньше):

    метод Тест(Имя: Строка): Строка
    пер Сообщение = "Привет, "
    пер Печать = метод() ->
    пер Сообщение = "Добро пожаловать, "
    возврат Сообщение + Имя + "!"
    ;
    возврат Печать()
    ;
  • Запрещается изменять переменные после их использования в лямбда-выражении (разрешается до):

    метод Тест(Имя: Строка): Строка
    пер Печать = метод() ->
    пер Сообщение = Имя + "!"
    возврат Сообщение
    пер Сообщение = "Привет, "
    ;
    возврат Печать()
    ;
  • Запрещается использовать в цикле переменные, объявленные во внешней области видимости:

    метод Тест(СписокИмен: Массив<Строка>): Массив<Строка>
    пер Сообщение = "Привет, "
    пер Имя = ""
    пер Печать = метод() ->
    пер Приветствие: Массив<Строка>
    для Имя из СписокИмен
    Приветствие.Добавить(Сообщение + Имя + "!")
    ;
    возврат Приветствие
    ;
    возврат Печать()
    ;
  • Запрещается изменять локальную переменную внутри цикла после захвата ее в лямбда-выражении:

    метод Тест()
    пер Число = 4
    для Индекс = 1 по 5
    Число += Индекс
    знч Лямбда = () -> Число
    ;
    ;

Отладка и стек исключений

В отладке и стеке исключений лямбда-выражения выглядят как обычные методы.

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

имя_метода_с_лямбда_выражением$имя_лямбда_выражения$имя_вложенного_лямбда_выражения$...

где:

  • имя_лямбда_выражения = <мета-имя><номер_лямбда_выражения>
  • мета-имя
    • имя параметра на текущем языке компиляции, в качестве которого передано лямбда-выражение;
    • иначе — отсутствует

Пример:

метод Калькулятор()
знч СоздатьСложение = метод() -> // Калькулятор$СоздатьСложение0
знч ВыполнитьСложение = (А: Число, Б: Число) -> А + Б // Калькулятор$СоздатьСложение0$ВыполнитьСложение0
возврат ВыполнитьСложение
;
знч СоздатьУмножение = метод() -> // Калькулятор$СоздатьУмножение1
знч ВыполнитьУмножение = (А: Число, Б: Число) -> А * Б // Калькулятор$СоздатьУмножение1$ВыполнитьУмножение0
возврат ВыполнитьУмножение
;
;

метод ПрименитьОперацию()
знч ВозвестиВКвадрат = ОбработатьОперацию(x -> x * x) // ПрименитьОперацию$Операция0
;

метод ОбработатьОперацию(Операция: (Число)->Число): (Число)->Число
возврат Операция
;

Примеры

См. также