Улучшаем 1С: Лямбда-функции

19.03.26

Разработка - Универсальные функции

Добавляем элементы функционального программирования в 1С.

В продолжение статьи про функции первого класса. Рекомендую ознакомиться прежде, чем читать эту.

Начав говорить о функциях первого класса, невозможно обойти стороной тему лямбда-функций.

 

Немного теории

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

 

Возможность реализации в 1С

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

Были и раньше попытки реализовать лямбда-подобные решения в 1С, например в этой статье (спойлер: там не настоящие лямбды, т.к. они не обладают свойствами функций первого класса).

 
 ДИСКЛЕЙМЕР

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

Во-1х, его невозможно отлаживать и синтаксический контроль для него работает только на этапе выполнения. Во-2х, динамическая компиляция требует дополнительного времени, которое прямо зависит от размера компилируемого текста, что может серьезно снижать производительность в высокочастотных участках программы. В-3х, его неудобно писать, для него не работает подсветка синтаксиса и в контексте окружающего "нормального" кода 1С он выглядит, как белая ворона.

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

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

В общем, все нижеследующее является скорее экспериментами и я никоим образом не рекомендую это к промышленному применению. Всё, что можно сделать при помощи лямбд, можно сделать и без лямбд, используя функтор.

Если сказанное до сих пор не отвратило вас от этой темы, читаем дальше.

 

Лямбда

Моя лямбда-функция реализована по образу и подобию Функтора. Работает только на сервере.

Пример тривиального лямбда-однострочника:

УмножительНаТри = Лямбда("а,м", "=а*м", , 3);

// использование:
Результат = УмножительНаТри.Вызвать(2); // Результат = 6

Пример посложнее, с накоплением данных в контексте замыкания:

Сумматор = Лямбда("а, @сум", "сум = сум + а; _=сум", , 0);

// использование:
Для каждого Элемент из Массив Цикл
    Сумматор.Вызвать(Элемент);
КонецЦикла;
СуммаЭлементов = Сумматор.Результат();

Здесь уже появляется непривычный синтаксис, всякие "@" и "_=". Сейчас разберем, что это такое.

 

Общий синтаксис создания лямбда-функции:

Функ = Лямбда(<ОписаниеПараметров>, <ТелоФункции>, <Параметр>, <Параметр>, ...);
  • <ОписаниеПараметров> - Строка - Список имен параметров функции, разделенных запятыми.
    Имя параметра может иметь префикс "@" - это означает, что параметр является возвращаемым. Параметры без такого префикса передаются "по значению".
  • <ТелоФункции> - Строка - Любая разрешенная последовательность выражений на встроенном языке 1С.
    Если функция должна вернуть результат вычисления первого выражения, то текст должен начинаться с символа "=" (как в примере "УмножительНаТри").
    Если первое выражение не возвращает результат, тогда его может вернуть любое из следующих выражений, присвоив его переменной с именем "_" (подчеркивание). Отсюда и выражение вида "_=сум" в примере "Сумматор". Жаль, что в динамически компилируемом коде нельзя использовать привычный оператор "Возврат", но я не вижу смысла тратиться на его имитацию.
  • <Параметр>, <Параметр>, ... - Произвольные параметры, максимум 8 штук. Они соответствуют параметрам функции и служат для создания контекста замыкания. Т.е. это значения, которые передает создающая сторона. Поскольку лямбда-функция не вызывается сразу в момент создания, значения параметров будут инкапсулированы вместе с функцией и использованы только в момент вызова.

Как и для функтора, в начале списка параметров рекомендуется располагать параметры, передаваемые вызывающей стороной, а в конце списка - параметры контекста замыкания, передаваемые создающей стороной.

 

Общий синтаксис вызова лямбда-функции:

Результат = Функ.Вызвать(<Параметр>, <Параметр>, ...);

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

 

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

Результат = Функ.Результат();

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

Значение = Функ.Параметр(<Индекс>);

 

Пример. Универсальный обход дерева

А вот так будет выглядеть пример с универсальным обходом дерева из предыдущей статьи. Напомню: нам нужно рекурсивно обходить все узлы дерева и с каждым узлом выполнять некоторое действие. Дерево одно, а действий много и все разные.

Пускай наше дерево содержит, среди прочих, колонки с числами и колонку Пометка (Булево). В примере реализуем несколько операций. Одна будет вычислять максимум значения численной колонки по всем узлам дерева. Вторая будет собирать массив всех узлов, где Пометка = Истина. Третья будет устанавливать значение Пометка во всех узлах.

 

Универсальная функция рекурсивного обхода остается неизменной:

Функция ОбходДерева(Узел, Действие)
	Для каждого Строка Из Узел.Строки Цикл
		Действие.Вызвать(Строка);
		ОбходДерева(Строка, Действие);
	КонецЦикла;
	Возврат Действие;
КонецФункции

 

А вот дальше мы сокращаем количество объявляемых функций вдвое, т.к. теперь функции действий можно описать в виде лямбда-выражений.

Функция МаксимумПоКолонке(Дерево, Колонка) Экспорт
	Возврат ОбходДерева(Дерево,
		Лямбда("Строка,Колонка,@Максимум",
			"Максимум = Макс(Строка[Колонка], Максимум); _= Максимум",, Колонка, 0))
		.Результат(); // результат последнего вызова функции действия
КонецФункции

Функция ПомеченныеУзлы(Дерево) Экспорт
	Возврат ОбходДерева(Дерево,
		Лямбда("Строка,Узлы",
			"Если Строка.Пометка Тогда Узлы.Добавить(Строка); КонецЕсли; _= Узлы",, Новый Массив))
		.Результат(); // результат последнего вызова функции действия
КонецФункции

Процедура УстановитьПометку(Дерево, Пометка) Экспорт
	ОбходДерева(Дерево, Лямбда("Строка,Пометка", "Строка.Пометка = Пометка",, Пометка));
КонецПроцедуры

 

Что под капотом?

 
Не сильно сложнее, чем Функтор

 

Вступайте в нашу телеграмм-группу Инфостарт

лямбда функции функциональное программирование

См. также

SALE! %

Мастера заполнения Поиск данных База данных Инструментарий разработчика Корректировка данных Универсальные функции Механизмы платформы 1С Подбор и обработка объектов 1С 8.3 1С 8.5 Платные (руб)

Infostart MagicInput улучшает подбор в полях ввода 1С: ищет по любой части названия и по нескольким ключевым фрагментам, распознаёт ввод в другой раскладке и показывает иконки/статусы объектов прямо в списке. Поддерживает вставку навигационной ссылки/представления документа для автоподбора; для разработчиков доступны поиск по GUID и полному имени предопределённого. Работает в управляемых формах и подключается в большинстве конфигураций 1С 8.3/8.5.

5000 4000 руб.

25.02.2026    1508    9    1    

11

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    4280    Abysswalker    11    

46

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    7939    DeerCven    15    

62

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    53732    dimanich70    84    

174

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7743    7    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    68310    atdonya    31    

72

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    9708    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SerVer1C 1059 19.03.26 10:22 Сейчас в теме
Пока жёлтые разработчики не встроят лямбды в платформу - это как 5-я нога собаке. Лямбда - это просто синтаксический сахар для упрощения записи кода. В вашем случае мы практически ничего не экономим.
3. korvintorson 60 19.03.26 11:14 Сейчас в теме
(1) Спасибо, кэп))
Всё, что можно сделать при помощи лямбд, можно сделать и без лямбд,
4. SerVer1C 1059 19.03.26 11:19 Сейчас в теме
(3) Если можно сделать, зачем оно? Лямбда - это анонимная функция - её можно вызвать практически в любом участке кода. С эской такое НЕ прокатит. Потому что НЕ поддерживает. Как изыскания чего-то необычного - да, это прикольно. Но не практично ни разу. Отладка этого чуда превратится в боль. И понимание для среднестатистического адынэсника займёт продолжительное время.
5. korvintorson 60 19.03.26 11:27 Сейчас в теме
(4) Если вы не знаете, зачем инструмент, значит у вас нет задач под этот инструмент. Все просто же.
6. SerVer1C 1059 19.03.26 11:30 Сейчас в теме
(5) Вы всё время говорите загадками. Мы (читатели) тут собрались НЕ на викторину. От вас мы ждём глубокое разжёвывание темы и примеры практического применения. Вы же пишите не для себя, а для людей!
7. korvintorson 60 19.03.26 11:57 Сейчас в теме
(6) Извините, что не оправдал Ваших ожиданий.
2. Трактор 1277 19.03.26 10:30 Сейчас в теме
А мне нравится. Пусть будет.
До сих пор жил без лямбд в 1С. Наверное, и дальше проживу. Но исследования в этом направлении мне симпатичны. Плюс.
korvintorson; +1 Ответить
8. tormozit 7370 19.03.26 12:12 Сейчас в теме
Поставил минус. Невозможно отладить и замерить скорость такого кода.
9. awk 745 19.03.26 12:18 Сейчас в теме
(8) А я не знаю, и плюсануть охота и минус влепить.
10. korvintorson 60 19.03.26 12:20 Сейчас в теме
(8) А я разве об этом не написал? А, точно, написал...
11. tormozit 7370 19.03.26 12:59 Сейчас в теме
(10) Ты частично написал о этих моментах в свернутом блоке. Высока вероятность что многие это не прочитают. Посмотри как я эти важные недостатки обозначил в родственной статье.
12. korvintorson 60 19.03.26 14:03 Сейчас в теме
(11) Ну ващето он развёрнутый. Так вижу. Может, сайт глючит и ты видишь его свернутым?
13. tormozit 7370 19.03.26 16:28 Сейчас в теме
(12) Проверил еще раз. Да теперь вижу что по умолчанию эта группа развернута. Но все же настолько важные недостатки лучше обозначать более явно в отдельном разделе с всем понятным заголовком, который позволяет быстро узнать об их наличии. Слово "дисклеймер" модное, но не всем понятное.
14. nikizan 19.03.26 16:38 Сейчас в теме
Не "УмножительНаТри", а "МножительНаТри", вероятно?
15. korvintorson 60 19.03.26 16:50 Сейчас в теме
(14) "Множитель" - это элемент операции умножения. А "умножитель" - это "совершитель операции умножения", да простит меня Александр Сергеевич.
16. nikizan 19.03.26 17:30 Сейчас в теме
(15) Если вы называете функцию, класс или переменную в коде (например, MultiplyByThree), то по-русски это будет именно «МножительНаТри» (калька структуры: MultiplierByThree). Вариант «Умножитель» звучит как не совсем правильный технический жаргон (хотя и встречается, например, в словосочетании «умножитель частоты» как устоявшийся термин).
17. korvintorson 60 19.03.26 18:07 Сейчас в теме
(16) Мне что-то вспомнился бородатый анекдот.

Собрал Сталин Политбюро и говорит: я собрал Вас, чтобы обсудить два моих предложения. Первое - расстрелять все правительство в полном составе. Второе - перекрасить Кремль в зеленый цвет.
Голос: Товарищ Сталин, а почему в зеленый?
Сталин: Я так и думал, что по первому пункту вопросов не будет.

У вас, как я понимаю, по остальным пунктам тоже вопросов нет?
Для отправки сообщения требуется регистрация/авторизация