В последнее время наметилась тенденция прникновения идей функционального программирования в массы. Для меня, как программиста 1С, интереснее всего повышение уровня абстракции при работе с табличными данными. Одно дело кодировать циклы со множеством переменных, которые меняют свое значение от итерации к итерации, а через месяц надо проводить «отладку глазами» (а то и на самом деле отладчик запускать), чтобы понять как эти циклы работают. Гораздо изящнее использовать готовые отлаженные алгоритмы, которые можно применить к таблице в целом, и получить ожидаемый результат.
Год за годом кодируя похожие и не очень циклы, я проникался желанием изменить что-то к лучшему в этом унылом процессе. Первое время меня вдохновляли обощенные алгоритмы STL С++. Потом для общего развития я изучал Haskell — этот язык действительно переворачивает восприятие.
Примерно 2 года назад я начал писать библитеку универсальных функций, которые применял в повседневной работе. Практика убедила меня, что подход работает, и приносит ощутимую пользу. А совсем недавно я открыл для себя язык LINQ, который используется на платформе .NET для унифицированной работы с коллекциями, формирования SQL-запросов и других полезных вещей. Я завидую белой завистью шарперам, у которых есть такой замечательный инструмент!
Изучив библиотеку стандартных операторов запроса, которая составляет ядро LINQ, я решил написать аналогичную библиотеку для 1С Предприятия 8.
Те наработки, которые у меня были до сих пор — выкидываю в помойку, т.к. они недостаточно системны и универсальны.
Основное препятствие для создания функционально-ориентированной библиотеки — это отстутствие во встроенном языке поддержки передачи функций как параметров.
Типичные алгоритмы для работы с коллекциями, требуют в качестве своих аргументов функций. Например, алгоритм фильтрации использует функцию условия, которая принимает элемент коллекции и возвращает булево. Преобразование полей таблицы требует еще более сложной функции. Функции, передаваемые в другую функцию в качестве параметра, в функциональном программировании называются замыканиями.
Я рассматривал разные варианты замены полноценных замыканий какими-либо суррогатами, вплоть до написания на встроенном языке виртуальной машины, интерпретирующей байт-код. В итоге пришел к выводу, что целесообразние всего воспользоваться возможностью языка вычислять выражения на встроенном языке, переданные в библиотеку виде строки (обоснование решения есть в проектной документации). Выглядит это так:
Символ подчеркивания обозначает параметр функции, в данном случае — строка исходной таблицы. Если функция принимает несколько параметров, они обозначаются "_0", "_1" и т.д. Функция, переданная в алгоритм, может использовать какие-то данные, известные на вызывающей стороне (контекст). Например:
Переменная "_к" получает значение контекста (обычно это структура), переданного вторым параметром функции Если. Для демонстрации более сложных конструкций приведу несколько надуманный пример:
Первый парметр задает ключевые поля соединения, и преобразует имена полей первой таблицы («Ссылка») в имена полей второй таблицы («Клиент»). Второй параметр задает выражения для полей результата (Клиент, ИНН, Заказано). «Клиенты» и «Заказы» это исходные таблицы значений.
Обработка данных в таблицах значений при помощи встроенного языка — не лучшее решение задачи. Втроенный язык работает значительно медленнее SQL запросов. Мы прибегаем к встроенному языку, только если данные получены не из базы или их объем невелик, а так же для нетривиальных операций (не все можно сделать запросами).
В свою библиотеку я включил функции, которые выглядят как универсальные алгоритмы, но на самом деле формируют запрос.
Для чего я это сделал? Приведенный пример быстрее и надежнее сделать конструктором запроса. Читается это все равно хуже, чем обычный язык запросов. Неужели только из любви к искуству? Не только. Эта часть библиотеки предназначена для сложный запросов, которые содержат повторяющиеся элементы, или переменное количество полей, подзапросов. Такие запросы обычно формируются контакенацией строк, что приводит к необходимости следить за синтаксисом, в особенности, за расстановкой запятых и скобок. Я надеюсь библиотека в этих случаях принесет больше пользы, чем вреда, хотя на практике я такой подход еще не тестировал.
На данный момент полностью готова проектная документация и заголовки функций. К реализации функций еще не приступал, но представляю ее себе с точностью до строчки — проект проработан очень подробно. Тестирование всего массива функций может составить бОльшую трудность. Я надеюсь получить отклик от коллег. Если будут обоснованные замечания / предложения, я готов откорркетировать интерфейс библиотеки, пока еще не приступил к реализации. Готовые модули тоже собираюсь публиковать. Планирую также расширять библиотеку добавлением специфических учетных алгоритмов: распределение по базе, распределение по фифо и т.д.
Ссылки:
Год за годом кодируя похожие и не очень циклы, я проникался желанием изменить что-то к лучшему в этом унылом процессе. Первое время меня вдохновляли обощенные алгоритмы STL С++. Потом для общего развития я изучал Haskell — этот язык действительно переворачивает восприятие.
Примерно 2 года назад я начал писать библитеку универсальных функций, которые применял в повседневной работе. Практика убедила меня, что подход работает, и приносит ощутимую пользу. А совсем недавно я открыл для себя язык LINQ, который используется на платформе .NET для унифицированной работы с коллекциями, формирования SQL-запросов и других полезных вещей. Я завидую белой завистью шарперам, у которых есть такой замечательный инструмент!
Изучив библиотеку стандартных операторов запроса, которая составляет ядро LINQ, я решил написать аналогичную библиотеку для 1С Предприятия 8.
Те наработки, которые у меня были до сих пор — выкидываю в помойку, т.к. они недостаточно системны и универсальны.
Основное препятствие для создания функционально-ориентированной библиотеки — это отстутствие во встроенном языке поддержки передачи функций как параметров.
Типичные алгоритмы для работы с коллекциями, требуют в качестве своих аргументов функций. Например, алгоритм фильтрации использует функцию условия, которая принимает элемент коллекции и возвращает булево. Преобразование полей таблицы требует еще более сложной функции. Функции, передаваемые в другую функцию в качестве параметра, в функциональном программировании называются замыканиями.
Я рассматривал разные варианты замены полноценных замыканий какими-либо суррогатами, вплоть до написания на встроенном языке виртуальной машины, интерпретирующей байт-код. В итоге пришел к выводу, что целесообразние всего воспользоваться возможностью языка вычислять выражения на встроенном языке, переданные в библиотеку виде строки (обоснование решения есть в проектной документации). Выглядит это так:
Отфильтровано = Таблицы.Если("_.Количество > 0",, ИсходнаяТаблица);
Символ подчеркивания обозначает параметр функции, в данном случае — строка исходной таблицы. Если функция принимает несколько параметров, они обозначаются "_0", "_1" и т.д. Функция, переданная в алгоритм, может использовать какие-то данные, известные на вызывающей стороне (контекст). Например:
Граница = 100;
Отфильтровано = Таблицы.Если("_.Количество > _к.Граница", Новый Структура("Граница", Граница), ИсходнаяТаблица);
Переменная "_к" получает значение контекста (обычно это структура), переданного вторым параметром функции Если. Для демонстрации более сложных конструкций приведу несколько надуманный пример:
СуммыЗаказовКлиентов = Таблицы.Соединение( "Клиент=_.Ссылка", "Клиент=_0.Ссылка |ИНН=_0.ИНН |Заказано = _1.Сумма",, Клиенты, Заказы );
Первый парметр задает ключевые поля соединения, и преобразует имена полей первой таблицы («Ссылка») в имена полей второй таблицы («Клиент»). Второй параметр задает выражения для полей результата (Клиент, ИНН, Заказано). «Клиенты» и «Заказы» это исходные таблицы значений.
Обработка данных в таблицах значений при помощи встроенного языка — не лучшее решение задачи. Втроенный язык работает значительно медленнее SQL запросов. Мы прибегаем к встроенному языку, только если данные получены не из базы или их объем невелик, а так же для нетривиальных операций (не все можно сделать запросами).
В свою библиотеку я включил функции, которые выглядят как универсальные алгоритмы, но на самом деле формируют запрос.
Запрос = Запросы.Выбрать( "Спецификации.Номенклатура КАК Материал, |(Заказы.Количество * Спецификации.Количество) КАК Количество", Запросы.Соединение( "Заказы", "Спецификации", "Заказы.Спецификация = Спецификации.Ссылка", Запросы.Из("Документ.ЗаказНаПроизводство.Продукция"), Запросы.Из("Справочник.Специфификации.ИсходныеКомплекующие") ) ) Результат = Запросы.Выполнить(,,Запрос);
Для чего я это сделал? Приведенный пример быстрее и надежнее сделать конструктором запроса. Читается это все равно хуже, чем обычный язык запросов. Неужели только из любви к искуству? Не только. Эта часть библиотеки предназначена для сложный запросов, которые содержат повторяющиеся элементы, или переменное количество полей, подзапросов. Такие запросы обычно формируются контакенацией строк, что приводит к необходимости следить за синтаксисом, в особенности, за расстановкой запятых и скобок. Я надеюсь библиотека в этих случаях принесет больше пользы, чем вреда, хотя на практике я такой подход еще не тестировал.
На данный момент полностью готова проектная документация и заголовки функций. К реализации функций еще не приступал, но представляю ее себе с точностью до строчки — проект проработан очень подробно. Тестирование всего массива функций может составить бОльшую трудность. Я надеюсь получить отклик от коллег. Если будут обоснованные замечания / предложения, я готов откорркетировать интерфейс библиотеки, пока еще не приступил к реализации. Готовые модули тоже собираюсь публиковать. Планирую также расширять библиотеку добавлением специфических учетных алгоритмов: распределение по базе, распределение по фифо и т.д.
Ссылки: