Язык декларативного программирования пример

Декларативное и императивное программирование

Или насколько неверным было мое представление о React

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Feb 11, 2018 · 4 min read

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Если вы читали документацию React, вы, вероятно, заметили, что React является декларативным. Обычно я пропускаю такого рода инфорамцию— согласен, не очень хорошая привычка. (Дайте мне скорее примеры кода!) Но как и всякий, кто начал изучать React сразу после колледжа, я часто думал о задачах неправильно.

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

Что такое декларативное и императивное программирование?

Согласно этой и этой информации:

Декларативное программирование — это парадигма программирования … которая выражает логику вычисления без описания его потока управления.
Императивное программирование — это парадигма программирования, в которой используются утверждения, которые изменяют состояние программы.

«Парадигма программирования» звучит супер претенциозно, и это, безусловно, фраза, которую любят профессоры моего колледжа. Если у вас есть образование в области информатики, эти определения могут быть не такими загадочными, но все же я считаю, что конкретные примеры способствуют большему пониманию.

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

Хорошо, вот метафора.

Декларативное программирование — это как попросить вашего друга нарисовать пейзаж. Тебе все равно, как он рисует, это зависит от него.

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

Продемонстрируй в коде

Ладно, ладно, вот несколько примеров кода. У нас есть кнопка, которая меняет цвет при нажатии. Я начну с императивного примера:

И пример нашего декларатвного React:

Возможно, различия незначительны. У нас по-прежнему есть логика, которая говорит: если красный, значит синий, но есть одна огромная разница. Пример c React никогда не затрагивает элемент. Он просто объявляет, что элемент должен отображаться с учетом нашего текущего состояния (далее — state). Он фактически не манипулирует самим DOM.

Использование прямого манипулирования DOM является ошибкой, которую я часто делал, когда впервые начал работать с React. Я заметил, что это тоже является проблемой для разработчиков, у которых большой опыт в jQuery.

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

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

Чего следует избегать

Я либо допустил, либо позволил допустить все эти ошибки в коде, над которым работал. Это всего лишь советы.

Источник

Декларативное программирование

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

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

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

«Чистое» функциональное программирование не содержит оператора присваивания, в нем вычисление любой функции не приводит ни к каким побочным эффектам, отличным от собственно вычисления ее значения. Разветвление вычислений основано на механизме обработке аргументов условного предложения, а циклические вычисления реализуются с помощью рекурсии.

Функциональное программирование весьма красиво и иногда в качестве первого языка программирования, изучаемого студентами, выбирается Haskell или Lisp. Для успешного овладения данным стилем программирования, впрочем, необходимо весьма глубокое понимание многих разделов математики.

Пример

Хорошей иллюстрацией функционального стиля программирования является программа на языке Haskell для получения всех пифагоровых троек чисел, не превосходящих заданного числа (пифагоровой тройкой называют три целых числа, являющихся сторонами некоторого прямоугольного треугольника).

Создайте файл с именем triads.hs в который поместите следующий текст:

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

Пример

Создайте файл с именем primes.hs и поместите в него следующие строки:

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Еще одной реализацией декларативного стиля является логическое программирование, основанное на логике предикатов, которое подробно рассматривается в следующей главе.

Пример

Для иллюстрации принципов логического программирования с использованием языка Пролог приведем программу, находящую решение известной головоломки «Ханойская башня», изобретенной французским математиком Люка в 1883 году и украшенной им же легендой.

«Где-то в непроходимых джунглях, недалеко от города Ханоя, есть монастырь бога Брамы. В начале времен, когда Брама создавал Мир, он воздвиг в этом монастыре три высоких алмазных стержня и на один из них возложил 64 диска, сделанных из чистого золота. Он приказал монахам перенести эту башню на другой стержень (в соответствии с правилами, разумеется). С этого времени монахи работают день и ночь. Когда они закончат свой труд, наступит конец света.»

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

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Поместите в файл с именем hanoi.pl следующий текст (символ % начинает комментарий, который не обязательно помещать в файл).

(не забудьте о точке в конце ввода). Ниже приводится порядок перемещения дисков, найденный этой программой.

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Источник

Императивное и декларативное программирование простым языком — объясняют эксперты

Императивное и декларативное программирование простым языком — объясняют эксперты

Начинающему программисту несложно запутаться в различных терминах — взять только объектно-ориентированное, динамическое, императивное и декларативное программирование. Спросили у экспертов, что из себя представляют два последних подхода.

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Павел Романченко

технический директор центра инновационных технологий и решений «Инфосистемы Джет»

Императивные языки, такие, как Java, Python, JavaScript, C, C++ занимают доминирующее положение в индустрии ПО, соответственно императивное программирование — самое распространённое. Смысл его в том, что императивная программа содержит прямые указания, что должен сделать компьютер и в каком порядке должны выполняться инструкции. Этот подход легко понять программисту, а компилятору — легко породить достаточно эффективный код.

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

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

Вообще в декларативных языках обычно отсутствует изменение переменных или обычно спрятано за каким-либо специальным механизмом.

Самый популярный язык РСУБД SQL так же является декларативным. На нём описывается конечный результат, а способ его получения генерируется сервером СУБД исходя из множества факторов.

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

Конечно, практически все основные языки сочетают в себе элементы и декларативного и императивного программирования. Огромное влияние оказывает функциональное программирование на JavaScript, Java, C++, C# и т.д.

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Роман Меньшиков

ведущий системный программист компании «Аэродиск»

Декларативное программирование — это парадигма программирования, в которой задаётся спецификация решения задачи: описывается, что представляет собой проблема и ожидаемый результат, но без описания способа достижения этого результата. Зачастую декларативные программы не используют понятия состояния и, в частности, не содержат переменных и операторов присваивания, обеспечивая ссылочную прозрачность. К подвидам декларативного программирования часто относят и функциональное программирование. Декларативные компьютерные языки часто не полны по Тьюрингу, так как теоретически не всегда возможно порождение исполняемого кода по декларативному описанию.

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

Несмотря на то, что исторически первым был применен декларативный подход в программировании, первые языки программирования компьютеров (машинный, ассемблер, фортран, алгол, кобол) были императивными в силу простоты подхода.

Сейчас широко распространены как узкоспециализированные декларативные языки программирования (HTML + CSS, SVG, VRML, SQL, lex/VACC), в том числе функциональные (Haskell, Erlang, Scala), так и императивные языки (C/C++/C#, Java, Go, Rust, Python). Однако практически все современные языки программирования общего назначения высокого уровня, за исключением некоторых функциональных, относятся к императивным языкам.

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

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

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

Михаил Козин

руководитель отдела разработки ECM «Техносерв Консалтинг»

Императивное программирование — это парадигма, основанная на составлении алгоритма действий (инструкций/команд), которые изменяют состояние (информацию/данные/память) программы. Первыми языками программирования, основанными на таком подходе, были машинные коды и ассемблеры. Фактически, программа на этих языках — это код, который выполняется компьютером сразу, без предварительной компиляции. Из языков высокого уровня, требующих компиляции исходного кода программы в машинный код (или интерпретации), к императивным можно отнести C, C++, Java.

Рассмотрим ещё один пример. Допустим, мы хотим приготовить обед.

В императивной парадигме это выглядит как-то так:

В декларативной: хочу на обед жареное мясо с овощами (неплохо звучит, правда? :)).

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

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

Источник

Императивное vs Декларативное программирование в javascript

Язык декларативного программирования пример. Смотреть фото Язык декларативного программирования пример. Смотреть картинку Язык декларативного программирования пример. Картинка про Язык декларативного программирования пример. Фото Язык декларативного программирования пример

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

К сожалению, вы, вероятно, сталкивались с определением вроде этого: «Дескать, вы знаете, императивное программирование похоже на то, как вы что-то делаете, а декларативное программирование больше на то, что вы делаете или что-то в этом духе».

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

Объедините это разочарование с «падением» самого слова «декларативный» до простого «хорошо» и внезапно ваш «синдром самозванца» станцует чечетку на вашей же уверенности, отчего вы можете сделать вывод, что не очень-то и любите программировать.

Не волнуйтесь, друзья. Я не знаю, что такое монада (monad), поэтому, вероятно, эта публикация поможет понять, что декларативный стиль — это нечто большее, чем просто «легко порассуждать о чем-то» и «хорошо«.

Сложность этой темы, как заметил Merrick, в том, что «это одна из тех вещей, которые вы понимаете интуитивно, но не можете объяснить».

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

Вернемся к изначальному определению, над которым я уже смеялся:

Императивное программирование похоже на то, «как» вы что-то делаете, а декларативное программирование больше похоже на «что» вы делаете».

Вообще, здесь припрятано немного хорошей информации. Сначала разберем достоинства этого определения вне контекста программирования и рассмотрим пример из «реальной жизни».

Вы решаете, что тратите слишком много времени на споры об «изнурительном JavaScript» («JavaScript Fatigue» ™) и реактивном функциональном программировании (Reactive Functional Programming), и ваш супруг достоин хорошего свидания. Вы решили пойти в Red Lobster, так как вы слушали много Бейонсе в последнее время. Вы приезжаете в Red Lobster, подходите к стойке и говорите…

Императивный подход (КАК): я вижу, что стол под знаком Gone Fishin’ свободен. Мы бы хотели подойти к нему и занять.

Декларативный подход (ЧТО): Стол для двоих, пожалуйста.

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

Задам вопрос и хочу, чтобы вы подумали об «императивном» и «декларативном» ответе.

«Я из Wal-Mart. Как мне добраться до вашего дома отсюда?»

Императивный ответ: выйдите с северного выхода паркинга и поверните налево. Затем по I-15 на юг, пока не доберетесь до съезда с Bangerter Highway. Поверните направо, как будто вам нужно в ИКЕА. Идите прямо и поверните направо на первом светофоре. Двигайтесь дальше к следующему светофору, поверните налево. Мой дом 298.

Декларативный ответ: мой адрес — 298 West Immutable Alley, Draper Utah 84020.

Вне зависимости от того, как я доберусь до вашего дома, машину-то я вожу. Собираюсь ли я водить «императивную машину «на ручке» или «декларативную автоматическую машину». Хватит метафор?

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

Декларативный ответ сотруднику Red Lobster предполагает, что тот знает все необходимые шаги, чтобы разместить нас. Знание адреса предполагает, что у вас есть какой-то GPS, который в курсе императивных шагов, как добраться до вашего дома.

Автоматический автомобиль имеет какой-то уровень абстракции над переключением передач.

Это было осознание, которое действительно заставило «щелкнуть» для меня, поэтому повторю: многие (если не все) декларативные подходы имеют в основе какую-то императивную абстракцию.

Если это предложение имеет для вас смысл, то вы молодец!

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

Подумайте здесь о типичном примере на SQL или HTML:

Изучив оба примера, у вас появится очень четкое понимание происходящего. Они оба декларативные. Они «обеспокоены» тем, ЧТО вы хотите сделать, а не КАК.

Вы описываете, чего вы пытаетесь достичь, не указывая как. Реализация выбора всех пользователей из Мексики была «абстрагирована». Вас не интересует, как веб-браузер анализирует вашу статью и отображает ее на экране. Ваше — это то, ЧТО такое пользователи из Мексики и новый заголовок и абзац на вашем сайте.

Все идет прекрасно! Перейдем к практической части на JavaScript.

Представьте, что вы на техническом собеседовании, а я интервьюер. Откройте консоль и решите задачи:

Рассмотрим наиболее распространенные подходы, которые также могут быть императивными:

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

Наиболее «очевидное общее» у них в описании — это КАК что-то делать. В каждом примере мы либо явно производим итерацию по массиву, либо явно излагаем шаги для реализации нужной нам функциональности.

Это может быть не столь очевидно, если вы не привыкли думать декларативно, или даже более конкретно — функционально. В каждом примере мы мутируем некоторую часть состояния (если Вы не знакомы с термином состояние (state), то это в основном информация о чем-то, что хранится в памяти, что должно звучать как очень похожее на переменные). В первых двух примерах мы создаем переменную results, а затем постоянно модифицируем ее. В третьем примере у нас нет переменных, но у нас есть состояние, живущее в DOM и затем мы изменяем это состояние в DOM.

Прозвучит немного субъективно, но для меня код выше не очень читаем. Я не могу так просто взглянуть на код и понять, что происходит. Мой мозг должен пройти сквозь код словно интерпретатор, принимая во внимание контекст, в котором живет код (еще один «минус» мутированных данных).

Ладно, хватит уже bullshita’ в коде) Рассмотрим некоторые декларативные примеры. Идея в том, чтобы исправить все проблемы сверху. Таким образом, каждый пример должен описывать, ЧТО происходит, не может мутировать состояние и должен легко читаться с первого взгляда.

Обратите внимание, что в первых двух примерах мы использовали методы map и reduce «из коробки» JavaScript. Это восходит к тому, о чем мы говорим снова и снова в этой статье, что большинство декларативных решений являются абстракцией над некоторой императивной реализацией.

В каждом примере мы описываем то, ЧТО мы хотим, а не КАК (мы не знаем, КАК реализованы map и reduce, но нам это и не важно). Мы не мутируем state. Все мутации абстрагированы в map и reduce. Это также и более читабельно (как только вы привыкнете к map и reduce, конечно).

А как насчет № 3? Ну, я немного обманул вас и использовал React, но обратите внимание, что все три императивные ошибки исправлены. Настоящая красота React в том, что вы можете создавать декларативные UI. Глядя на наш компонент Btn, я могу легко понять, как будет выглядеть UI. Другое выгодное отличие — вместо того, чтобы состояние (state) «жило» в DOM, оно «живет» в компоненте React.

Еще одним преимуществом декларативного кода является то, что программа может быть независимой от контекста. Это означает, что, поскольку ваш код связан с конечной целью, а не с шагами, которые необходимы для достижения цели, один и тот же код может быть переиспользован в разных программах и прекрасно работать.

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

Улучшить ваш код, сделав его более функциональным, как говорится, «не сложнее низко висящих фруктов» (или проще, чем «взять с полки пирожок» — по-русски, в тексте приведено английское популярное выражение — прим.переводчика).

Ниже я привел некоторые другие определения из сети, которые могут быть полезными. Или не быть.

Источник

Основы декларативного программирования на Lua

Луа (Lua) — мощный, быстрый, лёгкий, расширяемый и встраиваемый скриптовый язык программирования. Луа удобно использовать для написания бизнес-логики приложений.

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

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

Пример

В качестве наивного примера возьмём код создания диалогового окна с текстовым сообщением и кнопкой в императивном стиле:

function build_message_box ( gui_builder )

local my_dialog = gui_builder:dialog ( )

my_dialog:set_title ( «Message Box» )

local my_label = gui_builder:label ( )

my_label:set_text ( «Hello, world!» )

local my_button = gui_builder:button ( )

В декларативном стиле этот код мог бы выглядеть так:

build_message_box = gui:dialog «Message Box»

Гораздо нагляднее. Но как сделать, чтобы это работало?

Основы

Чтобы разобраться в чём дело, нужно знать о некоторых особенностях языка Луа. Я поверхностно расскажу о самых важных для понимания данной статьи. Более подробную информацию можно получить по ссылкам ниже.

Динамическая типизация

Важно помнить, что Луа — язык с динамической типизацией. Это значит, что тип в языке связан не с переменной, а с её значением. Одна и та же переменная может принимать значения разных типов:

Таблицы

Таблицы (table) — основное средство композиции данных в Луа. Таблица — это и record и array и dictionary и set и object.

Для программирования на Луа очень важно хорошо знать этот тип данных. Я кратко остановлюсь лишь на самых важных для понимания деталях.

Создаются таблицы при помощи «конструктора таблиц» (table constructor) — пары фигурных скобок.

Создадим пустую таблицу t:

Запишем в таблицу t строку «one» по ключу 1 и число 1 по ключу «one»:

Содержимое таблицы можно указать при её создании:

Таблица в Луа может содержать ключи и значения всех типов (кроме nil). Но чаще всего в качестве ключей используются целые положительные числа (array) или строки (record / dictionary). Для работы с этими типами ключей язык предоставляет особые средства. Я остановлюсь только на синтаксисе.

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

Следующие две формы записи эквивалентны:

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

При создании таблицы следующие две формы записи эквивалентны:

Аналогично для индексации при записи…

Функции

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

Функции в Луа можно создавать динамически в любом месте кода. При этом внутри функции доступны не только её аргументы и глобальные переменные, но и локальные переменные из внешних областей видимости. Функции в Луа, на самом деле, это замыкания (closures).

function make_multiplier ( coeff )

return function ( value )

return value * coeff

local x5 = make_multiplier ( 5 )

Важно помнить, что «объявление функции» в Луа — на самом деле синтаксический сахар, скрывающий создание значения типа «функция» и присвоение его переменной.

Следующие два способа создания функции эквивалентны. Создаётся новая функция и присваивается глобальной переменной mul.

function mul ( lhs, rhs ) return lhs * rhs end

mul = function ( lhs, rhs ) return lhs * rhs end

Вызов функции без круглых скобок

В Луа можно не ставить круглые скобки при вызове функции с единственным аргументом, если этот аргумент — строковый литерал или конструктор таблицы. Это очень удобно при написании кода в декларативном стиле.

print ( «Shopping list:» )

for name, qty in pairs ( items ) do

Цепочки вызовов

Как я уже упоминал, функция в Луа может вернуть другую функцию (или даже саму себя). Возвращённую функцию можно вызвать сразу же:

chain_print ( 1 ) ( «alpha» ) ( 2 ) ( «beta» ) ( 3 ) ( «gamma» )

В примере выше можно опустить скобки вокруг строковых литералов:

chain_print ( 1 ) «alpha» ( 2 ) «beta» ( 3 ) «gamma»

Для наглядности приведу эквивалентный код без «выкрутасов»:

local tmp1 = chain_print ( 1 )

local tmp2 = tmp1 ( «alpha» )

local tmp3 = tmp2 ( 2 )

local tmp4 = tmp3 ( «beta» )

local tmp5 = tmp4 ( 3 )

Методы

Объекты в Луа — чаще всего реализуются при помощи таблиц.

За методами, обычно, скрываются значения-функции, получаемые индексированием таблицы по строковому ключу-идентификатору.

Луа предоставляет специальный синтаксический сахар для объявления и вызова методов — двоеточие. Двоеточие скрывает первый аргумент метода — self, сам объект.

Следующие три формы записи эквивалентны. Создаётся глобальная переменная myobj, в которую записывается таблица-объект с единственным методом foo.

function myobj:foo ( b )

function myobj.foo ( self, b )

myobj [ «foo» ] = function ( self, b )

print ( self [ «a_» ] + b )

Примечание: Как можно заметить, при вызове метода без использования двоеточия, myobj упоминается два раза. Следующие два примера, очевидно, не эквивалентны в случае, когда get_myobj() выполняется с побочными эффектами.

Чтобы код был эквивалентен варианту с двоеточием, нужна временная переменная:

local tmp = get_myobj ( )

При вызове методов через двоеточие также можно опускать круглые скобки, если методу передаётся единственный явный аргумент — строковый литерал или конструктор таблицы:

Реализация

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

build_message_box = gui:dialog «Message Box»

Что же там написано?

Приведу эквивалентную реализацию без декларативных «выкрутасов»:

local tmp_1 = gui : label ( «Hello, world!» )

local tmp_2 = gui : button ( «OK» )

local button = tmp_2 ( < >)

local tmp_3 = gui : dialog ( «Message Box» )

Интерфейс объекта gui

Как мы видим, всю работу выполняет объект gui — «конструктор» нашей функции build_message_box(). Теперь уже видны очертания его интерфейса.

Опишем их в псевдокоде:

Декларативный метод

В интерфейсе объекта gui чётко виден шаблон — метод, принимающий часть аргументов и возвращающий функцию, принимающую остальные аргументы и возвращающую окончательный результат.

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

function build_message_box ( gui_builder )

local my_dialog = gui_builder:dialog ( )

my_dialog:set_title ( «Message Box» )

local my_label = gui_builder:label ( )

my_label:set_text ( «Hello, world!» )

local my_button = gui_builder:button ( )

Попробуем представить себе, как мог бы выглядеть метод gui:dialog():

return function ( element_list )

return function ( gui_builder )

local my_dialog = gui_builder:dialog ( )

element_list [ i ] ( gui_builder )

Ситуация с [gui_element] прояснилась. Это — функция-конструктор, создающая соответствующий элемент диалога.

Функция build_message_box() создаёт диалог, вызывает функции-конструкторы для дочерних элементов, после чего добавляет эти элементы к диалогу. Функции-конструкторы для элементов диалога явно очень похожи по устройству на build_message_box(). Генерирующие их методы объекта gui тоже будут похожи.

Напрашивается как минимум такое обобщение:

function declarative_method ( method )

return function ( self, name )

return function ( data )

return method ( self, name, data )

Теперь gui:dialog() можно записать нагляднее:

gui.dialog = declarative_method ( function ( self, title, element_list )

return function ( gui_builder )

local my_dialog = gui_builder:dialog ( )

element_list [ i ] ( gui_builder )

Реализация методов gui:label() и gui:button() стала очевидна:

gui.label = declarative_method ( function ( self, text, parameters )

return function ( gui_builder )

local my_label = gui_builder:label ( )

if parameters.font_size then

gui.button = declarative_method ( function ( self, title, parameters )

return function ( gui_builder )

local my_button = gui_builder:button ( )

— Так сложилось, что у нашей кнопки нет параметров.

Что же у нас получилось?

Проблема улучшения читаемости нашего наивного императивного примера успешно решена.

В результате нашей работы мы, фактически, реализовали с помощью Луа собственный предметно-ориентированный декларативный язык описания «игрушечного» пользовательского интерфейса (DSL).

Благодаря особенностям Луа реализация получилась дешёвой и достаточно гибкой и мощной.

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

Например, если на нашем микро-языке будут писать пользователи, нам понадобится поместить выполняемый код в песочницу. Также, нужно будет серьёзно поработать над понятностью сообщений об ошибках.

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

Полностью работающий пример можно посмотреть здесь.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *