Что такое декларативное программирование?
Я продолжаю слышать этот термин в нескольких разных контекстах. Что это?
18 ответов
Декларативное программирование - это когда вы пишете свой код таким образом, что он описывает то, что вы хотите сделать, а не то, как вы хотите это сделать. Это зависит от компилятора, чтобы выяснить, как.
Примерами декларативных языков программирования являются SQL и Prolog.
Другие ответы уже проделали фантастическую работу, объясняя, что такое декларативное программирование, поэтому я просто приведу несколько примеров того, почему это может быть полезно.
Независимость контекста
Декларативные программы не зависят от контекста. Поскольку они объявляют только то, что является конечной целью, но не промежуточные шаги для достижения этой цели, одну и ту же программу можно использовать в разных контекстах. Это трудно сделать с императивными программами, потому что они часто зависят от контекста (например, скрытого состояния).
принимать yacc
В качестве примера. Это генератор парсеров ака. компилятор компилятор, внешний декларативный DSL для описания грамматики языка, так что анализатор для этого языка может автоматически генерироваться из описания. Из-за его независимости от контекста вы можете делать много разных вещей с такой грамматикой:
- Создайте синтаксический анализатор C для этой грамматики (исходный вариант использования для
yacc
) - Создать синтаксический анализатор C++ для этой грамматики
- Создайте синтаксический анализатор Java для этой грамматики (используя Jay)
- Создать синтаксический анализатор C# для этой грамматики (используя GPPG)
- Создайте анализатор Ruby для этой грамматики (используя Racc)
- Создайте древовидную визуализацию для этой грамматики (используя GraphViz)
- просто сделайте красивую печать, необычное форматирование и подсветку синтаксиса самого исходного файла yacc и включите его в Справочное руководство в качестве синтаксической спецификации вашего языка
И многое другое...
оптимизация
Поскольку вы не прописываете компьютеру, какие шаги и в каком порядке предпринять, он может гораздо более свободно переставлять вашу программу, возможно, даже выполнять некоторые задачи параллельно. Хорошим примером является планировщик запросов и оптимизатор запросов для базы данных SQL. Большинство баз данных SQL позволяют отображать запрос, который они фактически выполняют, по сравнению с запросом, который вы просили их выполнить. Часто эти запросы не похожи друг на друга. Планировщик запросов принимает во внимание то, о чем вы даже не мечтали: например, задержку вращения диска, например, или тот факт, что какое-то совершенно другое приложение для совершенно другого пользователя просто выполнило аналогичный запрос и таблицу, которой вы являетесь. соединение с тем, что вы так усердно работали, чтобы избежать загрузки, уже есть в памяти.
Здесь есть интересный компромисс: машина должна работать усерднее, чтобы понять, как сделать что-то, чем это было бы на императивном языке, но когда она это понимает, у нее гораздо больше свободы и гораздо больше информации для оптимизации. этап.
Неплотно:
Декларативное программирование имеет тенденцию к:
- Наборы объявлений или декларативных операторов, каждое из которых имеет значение (часто в проблемной области) и может быть понято независимо и изолированно.
Императивное программирование имеет тенденцию к:-
- Последовательности команд, каждая из которых выполняет какое-либо действие; но которые могут иметь или не иметь значение в проблемной области.
В результате императивный стиль помогает читателю понять механику того, что система на самом деле делает, но может дать мало понимания проблемы, которую она призвана решить. С другой стороны, декларативный стиль помогает читателю понять проблемную область и подход, который система использует для решения проблемы, но менее информативен в механике.
Реальные программы (даже написанные на языках, которые поддерживают концы спектра, такие как ProLog или C), как правило, имеют оба стиля, представленные в различной степени в разных точках, чтобы удовлетворить различные сложности и коммуникационные потребности произведения. Один стиль не превосходит другой; они просто служат различным целям, и, как и во многих вещах в жизни, умеренность является ключевым фактором.
Вот пример.
В CSS (используется для стилизации HTML-страниц), если вы хотите, чтобы элемент изображения имел высоту 100 пикселей и ширину 100 пикселей, вы просто "объявляете", что вы хотите, следующим образом:
#myImageId {
height: 100px;
width: 100px;
}
Вы можете считать CSS декларативным языком "таблиц стилей".
Движок браузера, который читает и интерпретирует этот CSS, свободен, чтобы изображение выглядело таким высоким и таким широким, как ему хочется. Различные браузерные движки (например, движок для IE, движок для Chrome) будут выполнять эту задачу по-разному.
Их уникальные реализации, конечно, НЕ написаны на декларативном языке, а на процедурном, таком как Assembly, C, C++, Java, JavaScript или Python. Этот код представляет собой набор шагов, которые необходимо выполнить шаг за шагом (и может включать вызовы функций). Он может выполнять такие вещи, как интерполяция значений пикселей и рендеринг на экране.
Извините, но я должен не согласиться со многими другими ответами. Я хотел бы остановить это запутанное недоразумение определения декларативного программирования.
Определение
Ссылочная прозрачность (RT) подвыражений является единственным обязательным атрибутом декларативного выражения программирования, поскольку это единственный атрибут, который не используется в императивном программировании.
Другие процитированные атрибуты декларативного программирования происходят из этого RT. Пожалуйста, нажмите гиперссылку выше для подробного объяснения.
Пример электронной таблицы
В двух ответах упоминается программирование электронных таблиц. В тех случаях, когда программирование электронных таблиц (или формул) не имеет доступа к изменяющемуся глобальному состоянию, это декларативное программирование. Это потому, что изменяемые значения ячеек являются монолитным входом и выходом main()
(вся программа). Новые значения не записываются в ячейки после выполнения каждой формулы, поэтому они не изменяются в течение всей жизни декларативной программы (выполнение всех формул в электронной таблице). Таким образом, по отношению друг к другу формулы рассматривают эти изменчивые клетки как неизменные. RT-функции разрешен доступ к неизменяемому глобальному состоянию (а также к изменяемому локальному состоянию).
Таким образом, возможность изменять значения в ячейках при завершении программы (как вывод из main()
), не делает их изменяемыми хранимыми значениями в контексте правил. Ключевым отличием является то, что значения ячеек не обновляются после выполнения каждой формулы электронной таблицы, поэтому порядок выполнения формул не имеет значения. Значения ячеек обновляются после выполнения всех декларативных формул.
Декларативное программирование - это картинка, где императивное программирование - это инструкции для рисования этой картины.
Вы пишете в декларативном стиле, если вы "рассказываете, что это такое", а не описываете шаги, которые компьютер должен предпринять, чтобы добраться туда, куда вы хотите.
Когда вы используете XML для разметки данных, вы используете декларативное программирование, потому что вы говорите: "Это человек, у которого день рождения, и у него есть уличный адрес".
Некоторые примеры, где декларативное и императивное программирование объединяются для большего эффекта:
Windows Presentation Foundation использует декларативный синтаксис XML для описания того, как выглядит пользовательский интерфейс и каковы связи (привязки) между элементами управления и базовыми структурами данных.
Структурированные файлы конфигурации используют декларативный синтаксис (так же просто, как пары "ключ = значение"), чтобы определить, что означает строка или значение данных.
HTML помечает текст тегами, которые описывают роль каждого фрагмента текста по отношению ко всему документу.
Декларативное программирование - это программирование с декларациями, то есть декларативными предложениями. Декларативные предложения имеют ряд свойств, которые отличают их от императивных предложений. В частности, декларации:
- коммутативный (может быть переупорядочен)
- ассоциативный (можно перегруппировать)
- идемпотент (может повторяться без изменения значения)
- монотонный (декларации не вычитают информацию)
Важным моментом является то, что все это структурные свойства и ортогональны предмету. Декларативный не о "Что против как". Мы можем объявить (представить и ограничить) "как" так же легко, как и "что". Декларативный - это структура, а не содержание. Декларативное программирование оказывает существенное влияние на то, как мы абстрагируем и рефакторизируем наш код, и на то, как мы его модульно превращаем в подпрограммы, но не так сильно в модели предметной области.
Часто мы можем перейти от императивного к декларативному, добавив контекст. Например, из "Поверните налево. (... подождите...) Поверните направо". to "Боб повернет налево на пересечении Фу и Бара в 11:01. Боб повернет направо на пересечении Бара и Бэза в 11:06". Обратите внимание, что в последнем случае предложения являются идемпотентными и коммутативными, тогда как в первом случае перестановка или повторение предложений сильно изменили бы смысл программы.
Что касается монотонности, объявления могут добавлять ограничения, которые вычитают возможности. Но ограничения по-прежнему добавляют информацию (точнее, ограничения - это информация). Если нам нужны изменяющиеся во времени объявления, типично моделировать это с явной временной семантикой - например, от "шар плоский" к "шар плоский в момент времени T". Если у нас есть две противоречивые декларации, мы имеем несовместимую декларативную систему, хотя это может быть решено путем введения мягких ограничений (приоритетов, вероятностей и т. Д.) Или использования паранормальной логики.
С тех пор как я написал свой предыдущий ответ, я сформулировал новое определение декларативного свойства, которое приводится ниже. Я также определил императивное программирование как двойственное свойство.
Это определение лучше того, которое я дал в моем предыдущем ответе, потому что оно лаконично и носит более общий характер. Но это может быть более трудным для понимания, потому что значение теорем о неполноте, применимых к программированию и жизни в целом, людям трудно обернуть вокруг себя.
В приведенном объяснении этого определения обсуждается роль, которую чисто функциональное программирование играет в декларативном программировании.
Декларативный и императивный
Декларативное свойство является странным, тупым, и его трудно уловить в технически точном определении, которое остается общим и не двусмысленным, потому что это наивное представление о том, что мы можем объявлять значение (то есть семантику) программы, не вызывая непреднамеренных побочных эффектов. Между выражением смысла и избеганием непреднамеренных эффектов существует внутреннее противоречие, которое на самом деле вытекает из теорем о неполноте программирования и нашей вселенной.
Чрезмерно упрощенно, технически неточно и часто неоднозначно определять декларативное как"что делать" и обязательно как"как делать". Неоднозначный случай - это "что" - это "как" в программе, которая выводит программу - компилятор.
Очевидно, что неограниченная рекурсия, которая делает язык Тьюринга полным, также аналогично семантике, а не только синтаксической структуре оценки (операционная семантика). Это логически пример, аналогичный теореме Гёделя: "любая полная система аксиом также несовместна ". Обдумайте противоречивую странность этой цитаты! Это также пример, который демонстрирует, как выражение семантики не имеет доказуемой границы, поэтому мы не можем доказать 2, что программа (и аналогично ее семантике) останавливается, как теорема Халтинга.
Теоремы о неполноте проистекают из фундаментальной природы нашей вселенной, которая, как указано во Втором законе термодинамики, гласит: " Энтропия(или число независимых возможностей)стремится к максимуму навсегда". Кодирование и дизайн программы никогда не заканчиваются - она жива! - потому что она пытается удовлетворить потребности реального мира, а семантика реального мира всегда меняется и имеет тенденцию к увеличению возможностей. Люди никогда не перестают открывать новые вещи (в том числе ошибки в программах;-).
Чтобы точно и технически охватить это вышеупомянутое желаемое понятие в этой странной вселенной, которая не имеет границ (учтите, что "вне" нашей вселенной нет), требуется краткое, но обманчиво-непростое определение, которое будет звучать некорректно до тех пор, пока оно не будет объяснено глубоко.
Определение:
В декларативном свойстве может существовать только один возможный набор операторов, которые могут выражать каждую конкретную модульную семантику.
Императивное свойство 3 является двойственным, где семантика противоречива по составу и / или может быть выражена с помощью вариаций наборов утверждений.
Это определение декларативного является исключительно локальным в семантической области, что означает, что оно требует, чтобы модульная семантика поддерживала свое непротиворечивое значение независимо от того, где и как она была реализована и использована в глобальной области. Таким образом, каждая декларативная модульная семантика должна быть по сути ортогональной ко всем возможным другим, а не невозможным (из-за теорем о неполноте)глобальным алгоритмом или моделью для подтверждения последовательности, что также является точкой " Больше не всегда лучше" Роберта Харпера, профессора компьютерных наук в Университете Карнеги-Меллона, один из разработчиков Standard ML.
Примеры этой модульной декларативной семантики включают функторы теории категорий, например
Applicative
Номинальная типизация, пространства имен, именованные поля и по отношению к операционному уровню семантики, а затем чисто функциональное программирование.Таким образом, хорошо разработанные декларативные языки могут более четко выражать значение, хотя и с некоторой потерей общности в том, что может быть выражено, и в то же время выигрывают в том, что можно выразить с внутренней согласованностью.
Примером вышеупомянутого определения является набор формул в ячейках программы для работы с электронными таблицами, которые, как ожидается, не будут иметь одинакового значения при перемещении в разные ячейки столбцов и строк, то есть изменились идентификаторы ячеек. Идентификаторы ячеек являются частью и не являются лишними по отношению к предполагаемому значению. Таким образом, каждый результат электронной таблицы уникален по отношению к идентификаторам ячеек в наборе формул. Последовательная модульная семантика в этом случае заключается в использовании идентификаторов ячеек в качестве ввода и вывода чистых функций для формул ячеек (см. Ниже).
Язык гипертекстовой разметки, то есть HTML - язык статических веб-страниц - является примером высоко (но не совсем 3) декларативного языка, который (по крайней мере, до HTML 5) не обладал способностью выражать динамическое поведение. HTML, пожалуй, самый простой язык для изучения. Для динамического поведения императивный язык сценариев, такой как JavaScript, обычно сочетался с HTML. HTML без JavaScript соответствует декларативному определению, поскольку каждый номинальный тип (т. Е. Теги) сохраняет свое непротиворечивое значение в рамках композиции в правилах синтаксиса.
Конкурентное определение для декларативного - это коммутативные и идемпотентные свойства семантических операторов, то есть, что операторы могут быть переупорядочены и продублированы без изменения значения. Например, операторы, присваивающие значения именованным полям, могут быть переупорядочены и продублированы без изменения смысла программы, если эти имена являются модульными по отношению к какому-либо подразумеваемому порядку. Имена иногда подразумевают порядок, например, идентификаторы ячеек включают их столбец и положение строки - перемещение итога в электронной таблице меняет его значение. В противном случае эти свойства неявно требуют глобальной согласованности семантики. Как правило, невозможно разработать семантику операторов, чтобы они оставались непротиворечивыми при случайном порядке или дублировании, поскольку порядок и дублирование являются неотъемлемой частью семантики. Например, выражения "Foo существует" (или конструкция) и "Foo не существует" (и уничтожение). Если кто-то считает случайное несоответствие эндемичным для предполагаемой семантики, то он принимает это определение как достаточно общее для декларативного свойства. По сути, это определение является пустым как обобщенное определение, потому что оно пытается сделать последовательность ортогональной семантике, то есть бросить вызов факту, что универсум семантики динамически неограничен и не может быть охвачен в глобальной парадигме когерентности.
Требование коммутативных и идемпотентных свойств для (структурной оценки порядка) операционной семантики нижнего уровня преобразует операционную семантику в декларативную локализованную модульную семантику, например, чисто функциональное программирование (включая рекурсию вместо императивных циклов). Тогда порядок выполнения деталей реализации не влияет (то есть распространяется глобально) на согласованность семантики более высокого уровня. Например, порядок оценки (и теоретически также дублирования) формул электронной таблицы не имеет значения, потому что выходные данные не копируются на входы до тех пор, пока не будут вычислены все выходные данные, т.е. аналогично чистым функциям.
C, Java, C++, C#, PHP и JavaScript не являются особенно декларативными. Синтаксис Copute и синтаксис Python более декларативно связаны с намеченными результатами, то есть согласованной синтаксической семантикой, которая устраняет посторонние, так что можно легко понять код после того, как он его забыл. Copute и Haskell усиливают детерминизм операционной семантики и поощряют " не повторяться" (DRY), потому что они допускают только чисто функциональную парадигму.
2 Даже там, где мы можем доказать семантику программы, например, с помощью языка Coq, это ограничено семантикой, выраженной в типизации, и типизация никогда не сможет охватить всю семантику программы, даже для языков, которые не полный по Тьюрингу, например, с помощью HTML+CSS можно выразить несовместимые комбинации, которые, таким образом, имеют неопределенную семантику.
3 Многие объяснения неправильно утверждают, что только императивное программирование имеет синтаксически упорядоченные операторы. Я прояснил эту путаницу между императивным и функциональным программированием. Например, порядок операторов HTML не уменьшает согласованности их значения.
Изменить: я разместил следующий комментарий в блоге Роберта Харпера:
в функциональном программировании... диапазон изменения переменной является типом
В зависимости от того, как можно отличить функциональное от императивного программирования, ваш "присваиваемый" в императивной программе также может иметь тип, ограничивающий его изменчивость.
Единственное неразборчивое определение, которое я в настоящее время ценю для функционального программирования, - это: а) функции в качестве объектов и типов первого класса, б) предпочтение рекурсии над циклами и / или в) чистые функции, то есть те функции, которые не влияют на желаемую семантику программы, когда она запоминается (таким образом, совершенно чистого функционального программирования не существует в денотационной семантике общего назначения из-за воздействий операционной семантики, например, выделения памяти).
Идемпотентное свойство чистой функции означает, что вызов функции для ее переменных может быть заменен ее значением, что обычно не относится к аргументам императивной процедуры. Чистые функции кажутся декларативными по отношению к несоставленным переходам между типами ввода и результата.
Но состав чистых функций не поддерживает такую согласованность, потому что можно моделировать императивный процесс побочного эффекта (глобального состояния) на чистом функциональном языке программирования, например, IOMonad на Haskell, и, кроме того, совершенно невозможно предотвратить это в любой тьюринговский полный чисто функциональный язык программирования.
Как я писал в 2012 году, что похоже на аналогичное согласие с комментариями в вашем недавнем блоге, что декларативное программирование является попыткой уловить идею, что предполагаемая семантика никогда не бывает непрозрачной. Примерами непрозрачной семантики являются зависимость от порядка, зависимость от стирания семантики более высокого уровня на уровне операционной семантики (например , преобразования не являются преобразованиями, а уточненные обобщенные элементы ограничивают семантику более высокого уровня), а также зависимость от значений переменных, которые нельзя проверить (доказано). правильно) языком программирования.
Таким образом, я пришел к выводу, что только полные языки, не являющиеся тьюринговыми, могут быть декларативными.
Таким образом, одним однозначным и отличительным признаком декларативного языка может быть то, что его вывод может быть доказан как подчиняющийся некоторому перечисляемому набору порождающих правил. Например, для любой конкретной HTML-программы (игнорируя различия в способах расхождения интерпретаторов), которая не является сценарием (т. Е. Не завершена по Тьюрингу), ее выходная изменчивость может быть перечисляемой. Или, более кратко, программа HTML является чистой функцией ее изменчивости. То же самое относится к программе электронных таблиц, которая является чистой функцией ее входных переменных.
Поэтому мне кажется, что декларативные языки являются антитезой неограниченной рекурсии, т.е. согласно второй теореме Гёделя о неполноте самореференциальные теоремы не могут быть доказаны.
Лези Лэмпорт написал сказку о том, как Евклид мог обойти теоремы Гёделя о неполноте, примененные к математическим доказательствам в контексте языка программирования, для сравнения между типами и логикой (соответствие Карри-Говарда и т. Д.).
Представьте себе страницу Excel. С колонками, заполненными формулами для расчета налоговой декларации.
Вся логика объявлена в ячейках, порядок вычисления определяется самой формулой, а не процедурно.
Это своего рода декларативное программирование. Вы объявляете проблемное пространство и решение, а не поток программы.
Пролог - единственный декларативный язык, который я использую. Это требует другого типа мышления, но хорошо учиться, если просто подвергнуть вас чему-то другому, кроме типичного процедурного языка программирования.
Я усовершенствовал свое понимание декларативного программирования с декабря 2011 года, когда дал ответ на этот вопрос. Здесь следует мое текущее понимание.
Длинная версия моего понимания (исследования) подробно описана по этой ссылке, которую вы должны прочитать, чтобы получить глубокое понимание резюме, которое я приведу ниже.
В императивном программировании изменяемое состояние хранится и читается, поэтому упорядочение и / или дублирование инструкций программы может изменить поведение (семантику) программы (и даже вызвать ошибку, то есть непреднамеренное поведение).
В самом наивном и крайнем смысле (который я утверждал в моем предыдущем ответе) декларативное программирование (DP) избегает всех сохраняемых изменяемых состояний, поэтому упорядочение и / или дублирование программных инструкций НЕ МОЖЕТ изменить поведение (семантику) программы.,
Однако такое экстремальное определение не будет очень полезным в реальном мире, так как почти каждая программа использует хранимое изменяемое состояние. Пример электронной таблицы соответствует этому предельному определению DP, поскольку весь программный код выполняется до завершения с одной статической копией состояния ввода перед сохранением новых состояний. Затем, если какое-либо состояние изменяется, это повторяется. Но большинство реальных программ не могут быть ограничены такой монолитной моделью изменений состояния.
Более полезное определение DP состоит в том, что упорядочение и / или дублирование инструкций программирования не изменяет никакой непрозрачной семантики. Другими словами, не происходит скрытых случайных изменений в семантике - любые изменения в порядке команд программы и / или дублировании вызывают только предполагаемые и прозрачные изменения в поведении программы.
Следующим шагом было бы поговорить о том, какие программные модели или парадигмы помогают в DP, но здесь вопрос не в этом.
Это метод программирования, основанный на описании того, что что- то должно делать или быть, а не на том, как это должно работать.
Другими словами, вы не пишете алгоритмы, составленные из выражений, вы просто размечаете, как вы хотите, чтобы все было. Два хороших примера - это HTML и WPF.
Эта статья в Википедии - хороший обзор: http://en.wikipedia.org/wiki/Declarative_programming
Декларативное программирование - это "акт программирования на языках, которые соответствуют ментальной модели разработчика, а не операционной модели машины".
Разница между декларативным и императивным программированием хорошо иллюстрируется проблемой анализа структурированных данных.
Императивная программа будет использовать взаимно рекурсивные функции для ввода и генерации данных. Декларативная программа выражает грамматику, которая определяет структуру данных, чтобы затем их можно было проанализировать.
Разница между этими двумя подходами заключается в том, что декларативная программа создает новый язык, который более точно сопоставлен с ментальной моделью проблемы, чем ее основной язык.
Это может показаться странным, но я бы добавил Excel (или любую другую электронную таблицу) в список декларативных систем. Хороший пример этого приведен здесь.
Я бы объяснил это, так как DP - это способ выразить
- Выражение цели, условия для - что мы ищем. Есть один, может быть или много?
- Некоторые известные факты
- Правила, расширяющие известные факты
... и там, где есть механизм вычитания, обычно работающий с алгоритмом унификации, чтобы найти цели.
Насколько я могу судить, он начал использоваться для описания систем программирования, таких как Пролог, потому что пролог (предположительно) предназначен для объявления вещей абстрактно.
Это все больше значит очень мало, поскольку имеет определение, данное пользователями выше. Должно быть ясно, что между декларативным программированием на Haskell существует пропасть, в отличие от декларативного программирования на HTML.
Пара других примеров декларативного программирования:
- ASP.Net разметка для привязки данных. Например, он просто говорит: "Заполните эту сетку этим источником" и оставляет это системе, как это происходит.
- Выражения Linq
Декларативное программирование хорошо, потому что оно может помочь упростить вашу ментальную модель * кода, и потому что оно может в конечном итоге стать более масштабируемым.
Например, допустим, у вас есть функция, которая делает что-то для каждого элемента в массиве или списке. Традиционный код будет выглядеть так:
foreach (object item in MyList)
{
DoSomething(item);
}
Ничего страшного там нет. Но что, если вы используете более декларативный синтаксис и вместо этого определяете DoSomething() как действие? Тогда вы можете сказать это так:
MyList.ForEach(DoSometing);
Это, конечно, более кратко. Но я уверен, что у вас больше проблем, чем просто сохранение двух строк кода здесь и там. Производительность, например. По старинке обработка должна была выполняться последовательно. Что если у метода.ForEach() есть способ сообщить вам, что он может обрабатывать обработку параллельно, автоматически? Внезапно вы сделали свой код многопоточным очень безопасным способом и изменили только одну строку кода. И, собственно, есть расширение для.Net, которое позволяет вам это делать.
- Если вы перейдете по этой ссылке, это приведет вас к сообщению в блоге моего друга. Весь пост немного длинный, но вы можете прокрутить вниз до заголовка "Проблема" и найти его там без проблем.*
Это зависит от того, как вы отправляете ответ на текст. В целом вы можете посмотреть на программу с определенной точки зрения, но это зависит от того, под каким углом вы смотрите на проблему. Я начну с программы: Dim Bus, Car, Time, Height As Integr
Опять же, это зависит от того, что проблема в целом. Возможно, вам придется сократить его из-за программы. Надеюсь, что это помогает и нужна обратная связь, если это не так. Благодарю вас.