Примеры использования функциональных типов

Хранение метода в переменной

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

знч МетодДлина = (СтрокаТекста: Строка) -> СтрокаТекста.Длина()
   ...   
пер ДлинаСтроки = МетодДлина("моя строка")

Также это может быть ссылка на метод, в данном случае на метод ПолучитьДлину():

метод Пример()
   знч МетодДлина = &ПолучитьДлину
     ...
   пер ДлинаСтроки = МетодДлина("моя строка")
     ...   
;

метод ПолучитьДлину(СтрокаТекста: Строка): Число
   возврат  СтрокаТекста.Длина()
;

Ссылка на системный метод

Вы можете ссылаться не только на собственные методы, но и на системные. Например, можно сослаться на метод Строка.Длина():

знч МетодДлина = &Строка.Длина
   ...
пер ДлинаСтроки = МетодДлина("моя строка")

Вызов системного метода с параметром функционального типа

Некоторые системные методы имеют параметры функционального типа.

Например, метод Массив<тип-элемента>.СортироватьПо() имеет первый параметр, в который вы должны передать свое выражение. Это выражение должно вернуть то свойство элемента массива, по которому массив нужно отсортировать.

Пример использования этого метода выглядит следующим образом. Массив студентов, каждый из которых представлен структурой Студент, сортируется по полю Оценка этой структуры:

структура Студент
    знч Фамилия: Строка
    знч Оценка: Число
;

метод Пример()
    
    знч МассивСтудентов = [новый Студент("Иванов",    5),
                           новый Студент("Савинская", 4),
                           новый Студент("Сидорова",  5),
                           новый Студент("Булатов",   3),
                           новый Студент("Кузнецова", 5)]
                           
    МассивСтудентов.СортироватьПо(ПарСтудент -> ПарСтудент.Оценка) 

;

Передача метода в другой метод

Например, в метод приходят две строки (ПерваяСтрока, ВтораяСтрока), которые нужно сравнить между собой. Для выполнения сравнения вызывается метод СравнитьСтроки(), в котором описан алгоритм сравнения:

метод Пример()
   пер Результат = СравнитьСтроки(ПерваяСтрока, ВтораяСтрока)
;

метод СравнитьСтроки(Первая: Строка, Вторая: Строка): Булево
   возврат Первая.Длина() < Вторая.Длина()
;

1. Использование лямбда-выражения

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

метод Пример()
   пер Результат = СравнитьСтроки(
      ПерваяСтрока, 
      ВтораяСтрока, 
      (Строка1, Строка2) -> Строка1.Длина() < Строка2.Длина()
   )
;

метод СравнитьСтроки(
   Первая: Строка, 
   Вторая: Строка, 
   Сравнить: (Строка, Строка)->Булево
): Булево

   возврат Сравнить(Первая, Вторая)
;

Здесь в вызове метода СравнитьСтроки() написано лямбда-выражение:

(Строка1, Строка2) -> Строка1.Длина() < Строка2.Длина()

Оно означает, что передается метод с двумя параметрами. Этот метод сравнивает длину первого параметра с длиной второго параметра операцией «меньше» <.

В объявлении метода СравнитьСтроки() указано, что его третий параметр имеет функциональный тип:

Сравнить: (Строка, Строка)->Булево

Имя этого параметра — Сравнить, в него будет передан метод, который принимает два параметра типа Строка и возвращает значение типа Булево.

В теле метода СравнитьСтроки() написан вызов значения функционального типа:

Сравнить(Первая, Вторая)

Это вызывается метод, переданный в третьем параметре Сравнить. Ему передаются две строки, которые пришли в метод в первом и втором параметрах.

2. Использование ссылки на метод

Вместо лямбда-выражений можно использовать ссылки на существующие методы. Например, если алгоритм сравнения уже описан в методе СравнитьНаМеньше(), то вместо лямбда-выражения можно использовать ссылку на этот существующий метод.

метод Пример()
   пер Результат = СравнитьСтроки(ПерваяСтрока, 
                                  ВтораяСтрока, 
                                  &СравнитьНаМеньше)
    
   пер в = Результат
;

метод СравнитьНаМеньше(Первая: Строка, Вторая: Строка): Булево
   возврат Первая.Длина() < Вторая.Длина()
;

метод СравнитьСтроки(
   Первая: Строка, 
   Вторая: Строка, 
   Сравнить: (Строка, Строка)->Булево): Булево

   возврат Сравнить(Первая, Вторая)
;

Чтобы написать ссылку на метод перед именем метода нужно поставить амперсанд &.

Жизненный пример

По своей сути это предыдущий пример, который использует структуры данных и алгоритмы, приближенные к реальной жизни.

Существует список студентов, который нужно отфильтровать по вводимой пользователем оценке. Например, показать только отличников.

Каждый студент описывается элементом структуры Студент, у которой есть два поля: Фамилия и Оценка. Для хранения списка студентов используется массив Массив<Студент>. Список студентов выглядит следующим образом.

Список студентов формируется в отдельном методе ПолучитьМассив(), содержимое которого не имеет значения для этого примера:

пер Студенты = ПолучитьМассив()

1. Фильтрация обычным способом

Создайте метод фильтрации, который получает на входе массив студентов и оценку для фильтрации, сравнивает оценку каждого студента с введенной оценкой и возвращает полученный список студентов:

метод Фильтровать(Студенты: Массив<Студент>, Оценка: Число): Массив<Строка>
   пер СписокСтудентов: Массив<Строка>
   для Студент из Студенты
      если Студент.Оценка == Оценка
         СписокСтудентов.Добавить(Студент.Фамилия)
      ;
   ;
   возврат СписокСтудентов
;

Оценка, введенная пользователем, поступает в метод Пример(). В нем вызывается метод Фильтровать(), куда передается исходный список студентов и оценка:

метод Пример(Оценка: Число)
   пер Студенты = ПолучитьМассив()
   Фильтровать(Студенты, Оценка)
;

При вводе оценки 5, вы получите следующий результат:

Иванов
Сидорова
Кузнецова

Метод Фильтровать() всегда использует только одно выражение фильтрации — сравнение на равенство. Но что если нужно каждый раз использовать новое выражение для фильтрации? Для этого можно использовать лямбда-выражение, которое позволит каждый раз передавать в виде аргумента новое выражение фильтрации.

2. Использование лямбда-выражения

Измените метод Фильтровать(), добавьте ему третий параметр УдовлетворяетУсловию. Это будет параметр функционального типа, который будет принимать метод, описанный лямбда-выражением. В теле метода Фильтровать() этот метод будет принимать два других аргумента — студента и оценку:

метод Фильтровать(Студенты: Массив<Студент>, 
                  Оценка: Число, 
                  УдовлетворяетУсловию: (Студент, Число)->Булево): Массив<Строка>

   пер СписокСтудентов: Массив<Строка>
   для Студент из Студенты
      если УдовлетворяетУсловию(Студент,Оценка)
         СписокСтудентов.Добавить(Студент.Фамилия)
      ;
   ;
   возврат СписокСтудентов
;

Чтобы передать в метод Фильтровать() это лямбда-выражение, доработайте его вызов:

метод Пример(Оценка: Число)
   пер Студенты = ПолучитьМассив()
   Фильтровать(Студенты, 
               Оценка, 
               (ПарСтудент, ПарОценка) -> ПарСтудент.Оценка == ПарОценка)
;

При вводе оценки 5, вы получите тот же результат:

Иванов
Сидорова
Кузнецова

Таким образом в виде переменной функционального типа можно «снаружи» передать в метод любое выражение фильтрации.

3. Использование ссылки на метод

Как поступить в том случае, если метод, содержащий выражение фильтрации, уже существует? Например, он называется ИмеетОценку():

метод ИмеетОценку(Студент: Студент, Оценка: Число): Булево
   возврат Студент.Оценка == Оценка
;

Этот метод имеет два параметра типа Студент и Число и возвращает значение типа Булево, то есть соответствует функциональному типу, указанному для параметра УдовлетворяетУсловию:

УдовлетворяетУсловию: (Студент, Число)->Булево)

Передайте метод ИмеетОценку() в виде ссылки:

метод Пример(Оценка: Число)
   пер Студенты = ПолучитьМассив()
   Фильтровать(Студенты, Оценка, &ИмеетОценку)
;

При вводе оценки 5, вы получите тот же результат:

Иванов
Сидорова
Кузнецова