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

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

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

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

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

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

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

// 3-й аргумент — значение функционального типа (Число, Число)->Булево
Сравнить(2, 3, (x, y) -> x == y)

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

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

Синтаксис

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

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

Примеры:

// Один параметр
x -> x + 1

// Один параметр с типом
(x: Число) -> x + 1

// Два параметра
(x, y) -> x + y

// Два параметра с типом
(x: Число, y: Число) -> x + y

Полная форма

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

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

Примеры:

// Один параметр
метод(x) ->
возврат x + 1
;

// Один параметр с типом
метод(x: Число) ->
возврат x + 1
;

// Два параметра
метод(x, y) ->
возврат x + y
;

// Два параметра с типом
метод(x: Число, y: Число) ->
возврат x + y
;

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

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

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

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

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

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

    метод Скрипт(Имя: Строка): Строка
    пер Печать = метод() ->
    возврат Сообщение + Имя + "!"
    пер Сообщение = "Привет, " // Ошибка, т.к. переменная не определена
    ;
    возврат Печать()
    ;

    или

    метод Скрипт(Имя: Строка): Строка
    пер Печать = метод() ->
    возврат Сообщение + Имя + "!"
    ;
    пер Сообщение = "Привет, " // Ошибка, т.к. переменная не определена
    возврат Печать()
    ;
  • В циклах:

    метод Скрипт()
    пер Сообщение = "Привет, "
    пер Имя = ""
    пер СписокИмен = ПолучитьСписокИмен()
    пер Печать = метод() ->
    пер Приветствие: Массив<Строка>
    для Имя из СписокИмен // Ошибка, т.к. переменная уже определена
    Приветствие.Добавить(Сообщение + Имя + "!")
    ;
    возврат Приветствие
    ;
    пер Результат = Печать()
    ;

    или

    метод Тест()
    пер п = 4
    для i = 1 по 5
    п += i // Ошибка, т.к. переменная может меняться после захвата лямбдой на 2 шаге цикла
    знч Лямбда = () -> п
    ;
    ;

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

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

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

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

где:

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

Пример:

метод тест()
знч л0 = метод() -> // тест$0
знч л0л0 = () -> 5 // тест$0$0
;
знч л1 = метод() -> // тест$1
знч л1л0 = () -> 5 // тест$1$0
;
;

метод тест2()
знч л0 = лямбда1(x->x) // тест2$лямбда20
;

метод лямбда1(лямбда2: (Число)->Число): (Число)->Число
возврат лямбда2
;

Примеры

См. также