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

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

Операнд -> Операнд + 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) -> выражение

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

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

Примеры:

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

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

Лямбда инструкции:

  • один параметр:
    метод(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
;