Основная особенность логических языков программирования заключается в том что программа
Некоторое время назад я писал про «Интернациональное программирование на естественных языках», в которой попытался представить достойную цель для абстрактного языка программирования, попробовав примерить на него роль связующего звена между миром программистов с компьютерами и не программистов.
Но в результате оказалось, что это не нужно в принципе, т.к. «не программистам» просто не требуется учиться писать программы. А если иногда такое желание и возникает, то вполне хватает обычных формализованных языков программирования, которых уже сейчас насчитывается наверно более десяти тысяч.
И пользователи, как программисты, так и не программисты, просто хотят решать возникающие перед ними задачи. И хотя задачи бывают совершенно разные, но если способ (алгоритм) её решения известен, то выбрать язык для её решения не составит никакого труда.
За исключением одного класса задач. Задач, решение которых нельзя описать в виде алгоритма. Но можно указать некие критерии, которым должно удовлетворять искомое решение. Я имею ввиду логические языки программирования и Пролог, как самый яркий представитель этого класса.
Еще помню воспоминание из юности, когда удалось достать дискету с этим языком. Ух, с каким задором горели мои глаза, когда мне казалось, ну вот, еще чуть-чуть и будет создана система с базой знаний, у которой и можно будет получить заветный ответ 42 на любой вопрос.
Так почему этого так и не случилось? В чем проблема Пролога, да и любой системы / языка программирования, назначение которых анализировать факты и искать ответы на вопросы?
Эта проблема называется «Комбинаторный взрыв» — экспоненциальная (или более) зависимость времени работы алгоритма от количества входных данных. И есть как минимум два решения этой проблемы.
Подходы к написанию программ
Прежде чем переходить к частностям, следует сказать пару слов про парадигмы программирования. Обычно противопоставляют между собой два разных стиля в написании программ: императивный и декларативный.
Императивный — это классический вариант написания программы, при котором программист сам задает шаги алгоритма для получения конечного результата. А сам текст программы состоит из последовательности команд, которые читают, сохраняют и обрабатывают данные или вызывают другие команды.
Декларативный — в этом стиле программист описывает условия задачи и правила для получения требуемого результата, но не требуется детально описывать все шаги работы алгоритма, оставляя их на усмотрения компьютера.
Именно к декларативному стилю относится язык Пролог, да и все остальные логические языки программирования. К декларативному стилю написания программ следует относить и язык структурированных запросов (SQL).
И проблема под называнием «Комбинаторный взрыв» сильнее всего оказывает негативное влияние как раз на подобные языки. Ведь в императивном подходе программист сам отвечает за последовательность выполняемых команд, и если он запрограммировал алгоритм полного перебора всех возможных вариантов решений, то он сам себе злобный Буратино.
Другое дело, программирование в декларативном стиле. Разработчик хоть и может указать ограничения, которые следует применять при поиске решения, но это возможно только в том случае, когда известен алгоритм решения задачи. Но если алгоритм решения известен, то проще использовать императивный стиль, как раз и реализуя этот алгоритм!
Поэтому основное применения языков программирования в декларативном стиле — отказаться от необходимости описания четкого алгоритма поиска решения, отдав это компьютеру на откуп. Для которого самое простое решение «в лоб» — полный перебор возможных вариантов.
Именно в этом случае и начинается экспоненциальный рост времени выполнения алгоритма. И начиная с определенного порога, время ожидания ответа становится неприемлемым для реального использования. Это и означает «Комбинаторный взрыв», резкий («взрывной») рост времени выполнения алгоритма при увеличении размера входных данных.
Проблема поиска решений
В языке Пролог эта проблема решалась за счет использования механизма отката и отсечений. Иногда еще уточняли про «красное» и «зеленое» отсечение решений. Но в любом случае, это были алгоритмические механизмы для ограничения количества размера дерева возможных решений, а необходимость их применения все равно остается на программисте.
Но чтобы их правильно реализовывать, нужно знать алгоритм решения, что опять возвращает нас к утверждению о том, что если известен алгоритм, то и программировать его удобнее в императивном стиле.
А если полный алгоритм решения задачи не известен (или не подходит, например из-за большого времени для его работы), то в результате остается либо увеличивать производительность системы, чтобы сократить время выполнения алгоритма, либо искать другое решение, в том числе, сокращая вычислительную сложность поиска решений, например, исключая заведомо не подходящие данные, чтобы уменьшить возможные комбинации их перебора.
Масштабирование производительности
Увеличение производительности тоже бывает разным и работает не во всех случаях. Вертикальное масштабирование производительности одного узла вычислительной среды имеет свой естественный предел. И даже многократное увеличение скорости работы компьютера может лишь отдалить порог терпения пользователя при ожидании получения результата, но не в состоянии принципиально решить саму проблему.
Другое дело горизонтальное масштабирование, при котором выполнение алгоритма запускается на отдельных узлах, которые параллельно решают одну и ту же задачу. Такой способ масштабирования позволяет уже значительно сократить время получения итогового результата для сложных вычислительных задач. И хотя это способ является решением «в лоб», но успехи в области data science доказывают успешность такого подхода.
Конечно, у горизонтального масштабирования тоже есть подводные камни. В первую очередь, сам алгоритм должен допускать возможность параллельного выполнения независимо от других узлов. Также требуется автоматизация управления заданиями, самими вычислительными узлами, да и всей системой в целом.
Тут частично может помочь парадигма функционального программирования, которая ограничивает результат вычисления функций только входными параметрами и результатом выполнения других функций, но сам результат не зависит от состояния системы или иных внешних данных.
Поиск обобщенного решения
Вторым способом решения решения проблемы комбинаторного взрыва является уменьшение вычислительной сложности решения. Тут имеется ввиду не выбор другого алгоритма или решение задачи в символьном виде. Если такое возможно, то все опять сразу сведется к императивному стилю программированию.
Я имею ввиду возможность поиска самого алгоритма решения. Точнее не совсем алгоритма, а возможность применения к входным данным различные методы отбора, чтобы исключить необходимость их полного перебора. По сути, это сводится к применению различных методов и механизмов обработки входных данных с учетом различных закономерностей.
Это возможно как алгоритмическими методами (откат и отсечение в Прологе), так и с применением машинного обучения, которое очень хорошо справляется с поиском различных закономерностей.
Естественно, такой способ подходит не для всех классов задач.
Он не подходит для выявления ВСЕХ возможных решений. Но там где это не требуется, подобные способы уменьшения вычислительной сложности имеют право на существование.
Например, не требуется искать все возможные лекарства от конкретной болезни, достаточно одного, с учетом определенных ограничений, которое гарантированно подействует.
К тому же, даже при нахождении частных решений, всегда существует шанс, что с их помощью получится увидеть не очевидные на первый взгляд закономерности, которые помогут показать новые пути алгоритмического уменьшения вычислительной сложности основной задачи.
Область не решаемых задач
Как вы считает, а реально ли создать язык логического программирования, который бы сам умел автоматизировать поиск решений для задач подобных классов? Или хотя бы имел в своем арсенале встроенные механизмы для автоматизации подобной деятельности?
Как часто вы сталкивались с непонятным термином «логическое программирование» и не могли понять, что это? Сегодня мы окончательно определимся с тем, что такое языки программирования логического типа, и рассмотрим примеры таких языков.
Прежде чем начинать обзор языков, необходимо сначала узнать, что это такое и зачем оно нужно.
Что такое логическое программирование?
Это подход к программированию, основанный на доказательстве теорем и выводе информации на основе фактов. Вывод результата является побочным продуктом работы программы. Логическое программирование основано на теории математической логики, включает в себя раздел дискретной математики и некоторые другие.
В чем различия?
Многие объединяют логические языки программирования с функциональными, однако они обладают некоторыми различиями, о которых мы далее поговорим.
В отличие от функциональных, логические очень хорошо подходят для создания искусственного интеллекта. Также иногда очень удобно, когда в языке переменные не делятся на входные и выходные. Именно в таких языках это и происходит, что иногда упрощает работу с кодом. Опять же программы являются более быстрыми и выполняют свои задачи иногда эффективнее. Основная особенность логических языков программирования заключается в том, что программа представляет определенные отношения между элементами.
Однако существуют и минусы. Не все можно описать с помощью логики, поэтому в таких программах функции будут работать не всегда так, как нужно, или вообще не будут работать.
Кому полезно учить языки программирования логического типа?
Стоит сразу ответить на вопрос: учить данные языки полезно всем – от школьника до человека в возрасте. Ведь логические языки программирования способны буквально заставить наш мозг думать логически. Также такие языки будут очень полезны в создании искусственного интеллекта или при работе с данными.
Логические языки программирования
Таких языков не так много, и они отличаются между собой. Мы поговорим только о двух, а начнем с того, с которого началась эра логических языков, и имя ему Prolog.
Данный язык был разработан в 1972 году Аленом Колмероэ и является актуальным и свежим и на сегодняшний день. Хоть это и не самый простой язык в плане синтаксиса, но зато весьма полезен в понимании логики компьютера. Посмотрите, как выглядит код, описывающий книгу:
Такое описание довольно просто понять и разобрать что к чему. Именно поэтому обучение подобному языку дальше не составит огромных трудностей и не потребует танцев с бубном.
Давайте дальше разберем его плюсы и минусы.
1. Код легко понять и запомнить.
Как уже упоминалось выше, код в языке Prolog пишется не так уж и сложно. Он довольно прост, в понимании обычного пользователя.
2. Выражения и факты.
Данный язык можно использовать без каких-либо вычислений, опираясь только на выражения и факты.
3. Путь не влияет.
Какой бы путь к реализации вашей задумки вы ни выбрали, он практически никогда не влияет на результат выполнения программы.
1. Слабые инвестиции.
Из-за того, что этот язык мало поддерживают в материальном плане – он развивается довольно медленно, маленькими шажками.
2. Невозможность создания комплексных программ.
Данный язык будет невозможно использовать без связки с другим, если вы хотите создавать более сложные программы с большим функционалом.
3. Вычислительные операции.
Для вычислительных операций опять же придется использовать другие языки.
Пошедший от языка Prolog, Mercury создан, чтобы решить две проблемы, связанные с популярным языком программирования.
Логические языки программирования довольно сильно уступают в производительности императивному типу.
В языках такого типа уходит довольно много времени на отладку программы, а также производится меньше проверок на ошибки в программе, из-за чего порой происходят неприятности.
Пример кода на Mercury:
Синтаксис и понимание данного языка, как можно заметить, весьма отличаются от «Пролога», что немного усложняет обучение, однако некоторые его плюсы, помогающие решить проблемы «Пролога», весьма полезны.
Заключение
Языком логического программирования является язык, который обрабатывает выражения и факты, а побочным эффектом выдает результат работы данной программы. Такие языки весьма полезны в создании искусственного интеллекта и работы с данными, однако редко применяются без сторонних языков программирования.
Как часто вы сталкивались с непонятным термин «логическое программирование» и не могли понять, что это? Сегодня мы, наконец, решить, с какой из языков программирования логического типа, и рассмотрим примеры таких языков.
Прежде чем начать обзор языков, вы должны сначала знать что это такое и зачем оно нужно. Что такое логическое программирование?
Это подход к программированию, основанный на доказательстве теорем и выводе информации на основе фактов. Результат-это побочный эффект программы. Логическое программирование основано на теории, математической логики, включает в себя раздел дискретной математики и некоторые другие. Каковы различия?
Многие объединяют логических языков программирования функциональные, но имеют некоторые различия, о которых мы ниже и поговорим.
В отличие от функциональных, логических очень подходит для создания искусственного интеллекта. Кроме того, иногда это очень полезно, когда в языке переменные не делятся на входящие и исходящие. И в этих языках, и бывает, что иногда упрощает работу с кодом. Даже в этом случае программы выполняются быстрее и выполняют свои задачи, иногда более эффективно. Основная особенность логических языков программирования заключается в том, что программа-это определенные отношения между элементами.
Тем не менее, есть и недостатки. Не все можно описать с помощью логики, поэтому, в таких программах всегда работают так, как нужно, или не работают вообще. Кому полезно изучать языки программирования логического типа?
Стоит сразу ответить на вопрос: выучить этот язык полезно всем – от студента до человека в возрасте. Потому, что логика языки программирования способны в буквальном смысле заставить наш мозг думать логически. Даже языки, будут очень полезны в создании искусственного интеллекта, или когда вы работаете с данными. Логические языки программирования
Таких языков не так много, и они отличаются между собой. Это говорит лишь о двух, и для начала, с которого началась эра логических языков, и его имя Prolog.
Этот язык был разработан в 1972 году Аллен Преодолеть и это важно, и свежее, и до сегодняшнего дня. Даже если это не самый простой язык в плане синтаксиса, но это очень полезно в понимании логики компьютера. Посмотрите, как выглядит код, который описывает книгу:
книга( 'Имя', '2009', 'Monza', автор (Первый автор', 'По мнению автора' ) ).
Такое описание является достаточно простой, чтобы понять и проанализировать, что. Вот почему обучение такой язык не будет огромных трудностей и не требует танцев с бубном.
Идем далее проанализировать свои плюсы и минусы.
1. Код легко понять и запомнить.
Как уже упоминалось ранее, код на языке Prolog писал не так уж и сложно. Это довольно простой, в понимании обычного пользователя.
2. Выражения и факты.
Этот язык можно использовать без каких-либо расчетов, полагаясь только на выражение мнений и фактов.
3. Пути, не интересует.
Любой путь к реализации вашей идеи, что вы выбираете, это практически никогда не влияет на результат выполнения программы.
1. Слабые инвестиции.
В связи с тем, что этот язык мало поддерживают в материальном плане – растет очень медленно, маленькими шагами.
2. Невозможность создания сложных программ.
Этот язык не будет, можно использовать без связки с другим, если вы хотите создать более сложные программы с большей функциональностью.
3. Вычислительные операции.
Для расчета, даже в этом случае вам придется использовать другие языки.
Ушел от языка Prolog, Mercury создана, чтобы решить две проблемы, связанные с популярным языком программирования.
Логические языки программирования гораздо меньше, чем с точки зрения производительности важно, типа.
В языках этого типа уходит много времени для отладки программы, так как происходит меньше проверок на ошибки в программе, что иногда возникают проблемы.
Пример кода Mercury:
:- module привет. :- interface. :- import_module я. :- pred main(я. я::ну-нс) is det. :- implementation. main(!Я) :- я.write_string("Hello, World! "что !Я).
Синтаксис и понимание этого языка, как вы можете заметить, сильно отличается от «Пролога», который является немного сложнее образование, тем не менее, некоторые свои плюсы, позволяют решить проблему «Пролог», это очень полезно. Заключение
Язык программирования-это язык, который управляет выражения и факты, и побочный эффект дает правильный результат работы программы. Эти языки являются очень полезными в создании искусственного интеллекта и работы с данными, однако, редко применяются без сторонних языков программирования. Автор: Владислав Астрахань 14 Августа, 2018
Неклассифицированные языки
ABAP/4
Awk
FoxPro
Tcl/Tk
ML
MQL4
PostScript
PL/SQL
Clarion
Clean
Clipper
Curry
Gentee
GPSS
DataFlex
Erlang
Forth
JOVIAL
Jython
Mercury
Linda
Occam
ПЛ/1
PL/M
Pixilang
Планкалкюль
Scala
xBase
Progress 4gl
X++
Sieve
PureBasic — компилируемая модификация Basic
Ассемблеры
Visual DataFlex
Робик
Рапира
УА (Упрощённый Алгол)
Nemerle
ДРАКОН
Структурные языки программирования
Алгол
Алгол 68
Basic
QBASIC
Фортран
REXX
sh
Фокал
Cg
Процедурные языки программирования
Алгоритмический язык
Си
КОБОЛ
Limbo
Lua
Malpe
MATLAB
Модула-2
Паскаль
Глагол
Логические языки программирования
Prolog
Программирование в ограничениях
Mozart
Функциональные языки программирования
Лисп
Лого
Dylan
Haskell
OCaml
Scheme
РЕФАЛ
АПЛ
J
ФОЛИ=ФОрт+ЛИсп
Языки программирования для промышленной автоматизации
(стандарта IEC61131-3)
FBD
IL
ST или SCL
Sequential Function Chart
Ladder Diagram
SPCLK
Эзотерические языки программирования
Byter
Brainfuck
Befunge
INTERCAL
Whitespace
низкого уровня
один Асемблер
высокого уровня С++ делфи Паскаль вижуал бейсик кобол пхп лисп и тд их оч много
Основные языки программирования
Для написания текста любой компьютерной программы используют один из многочисленных языков программирования. Все они представляют собой наборы определенных команд – операторов, а также описаний. Как правило, основой этих команд являются английские слова, поэтому, если вы знаете английский язык, читая текст программы вы даже сможете понять, что будет делать компьютер по той или иной команде. Впрочем, компьютер английского языка, в отличие от вас, не знает – чтобы он понял их, на машинный язык эти команды «переводит» компилятор. Для каждого из языков программирования существует свой компилятор.
Первые языки программирования, в том числе: ADA, Basic, Algol, Fortran и прочие, бывшие популярными в 60-70 г. г., уже давно не используются, а вот С++, например, созданный в 1983 году, остается и сегодня востребованным, многие специальные программные продукты написаны на нем. По-прежнему востребован Basic, появившийся в 1991 году; а также Pascal (среда разработки Delphi), Java, JavaScript и Ruby, созданные в 1995 году. Из новых можно назвать ActionScript и Nemerle, которые появились в 1998 и 2006 году, соответственно.
Перечисленные языки программирования по-прежнему актуальны, поскольку они постоянно модифицируются, и новые их версии адаптируются к тем потребностям, которые существуют на сегодняшний день. Это в первую очередь относится к языку С++. Несмотря на то что в некоторых случаях программный код, скомпилированный на этом языке, получается довольно громоздким, использование готовых шаблонов помогает решить эту проблему, значительно улучшая быстродействие программных продуктов.
Среда разработки Visual Basic, разработанная знаменитой Microsoft, также используется большинством программистов, позволяя не только создавать компактный программный код на языке Basic, но и использовать удобный встроенный конструктор для пользовательского интерфейса. А вот для создания веб-сайтов программисты используют язык РНР, считающийся универсальным и работающий с любыми операционными системами. Его применяют и как конструктор пользовательского интерфейса. Однако к существенным недостаткам этого языка можно отнести то, что код, написанный в более ранних версиях, поддерживаться новыми не будет.
Java также способен работать на любой платформе, но для написания программ на этом языке необходимо пользоваться тем его диалектом, который предназначен для данного типа программных продуктов. Многофункциональностью, универсальностью и простотой отличаются языки программирования Pascal и JavaScript. Первый чаще используется для создания программных продуктов под ОС, например Total Commander и QIP, а на втором написана большая часть современных браузеров.
Язык APL оказал влияние на функциональное программирование и стал первым языком, поддерживавшим обработку массивов [9].
Язык ПЛ/1 (NPL) был разработан в 1960-х годах как объединение лучших черт Фортрана и Кобола.
Язык Snobol, разработанный и совершенствуемый в течение 1960-х годов, ориентированный на обработку текстов, ввёл в число базовых операций языков программирования сопоставление с образцом [10][11][12].
Язык Симула, появившийся примерно в это же время, впервые включал поддержку объектно-ориентированного программирования. В середине 1970-х группа специалистов представила язык Smalltalk, который был уже всецело объектно-ориентированным.
Неклассифицированные языки
ABAP/4
Awk
FoxPro
Tcl/Tk
ML
MQL4
PostScript
PL/SQL
Clarion
Clean
Clipper
Curry
Gentee
GPSS
DataFlex
Erlang
Forth
JOVIAL
Jython
Mercury
Linda
Occam
ПЛ/1
PL/M
Pixilang
Планкалкюль
Scala
xBase
Progress 4gl
X++
Sieve
PureBasic — компилируемая модификация Basic
Ассемблеры
Visual DataFlex
Робик
Рапира
УА (Упрощённый Алгол)
Nemerle
ДРАКОН
Структурные языки программирования
Алгол
Алгол 68
Basic
QBASIC
Фортран
REXX
sh
Фокал
Cg
Процедурные языки программирования
Алгоритмический язык
Си
КОБОЛ
Limbo
Lua
Malpe
MATLAB
Модула-2
Паскаль
Глагол
Логические языки программирования
Prolog
Программирование в ограничениях
Mozart
Функциональные языки программирования
Лисп
Лого
Dylan
Haskell
OCaml
Scheme
РЕФАЛ
АПЛ
J
ФОЛИ=ФОрт+ЛИсп
Языки программирования для промышленной автоматизации
(стандарта IEC61131-3)
FBD
IL
ST или SCL
Sequential Function Chart
Ladder Diagram
SPCLK
Эзотерические языки программирования
Byter
Brainfuck
Befunge
INTERCAL
Whitespace
Начну, пожалуй, с представления читателя этой статьи, так как ничто не приковывает внимание к тексту более, чем сопереживание главному герою, тем более, в его роли сейчас выступаете Вы. Вероятно, услышав или прочитав однажды словосочетание "логическое программирование" и преисполнившись интересом, Вы как настоящий или будущий программист направились в Google. Первая ссылка, разумеется, ведёт на Википедию - читаем определение:
Логи́ческое программи́рование — парадигма программирования, основанная на автоматическом доказательстве теорем, а также раздел дискретной математики, изучающий принципы логического вывода информации на основе заданных фактов и правил вывода. Логическое программирование основано на теории и аппарате математической логики с использованием математических принципов резолюций.
"Мда" - думаете Вы, и этим все сказано. Сложно! И тут наш отважный герой должен бы был перейти по второй ссылке, но я позволю себе сделать небольшую вставку, описав главное действующее лицо: Вы, по моей задумке, новичок в программировании, а даже если и нет, то точно не знакомы с логическим его обличием. Если же читатель уже несколько (или даже много) искушен знаниями в этой области, то рекомендую прочитать статью Что такое логическое программирование и зачем оно нам нужно, раз уж в вас горит интерес и любопытство к теме, а изучение материала ниже оставьте менее опытным коллегам.
Итак, пришло время второй ссылки. Что это будет? Статья на Хабре? Может быть статья на ином ресурсе? Прочитав пару первых абзацев на разных сайтах, вы, скорее всего, мало что поймете, так как, во-первых, материал обычно ориентирован на знающего читателя, во-вторых, хорошей и понятной информации по теме не так много в русскоязычном интернете, в-третьих, там почему-то постоянно речь идёт о некоем "прологе" (речь о языке программирования Prolog, разумеется), но сам язык, кажется, использует мало кто (почётное 35 место в рейтинге TIOBE). Однако наш герой не теряет мотивации и, спустя некоторое время, натыкается на эту самую статью, желая, все-таки понять:
Что такое логическое программирование
Какова история его создания и фундаментальные основы (серьезно, какому новичку это может быть интересно?)
Зачем и где его применяют
Стоит ли лично вам его изучать
Что ж, постараюсь ответить просто и понятно, обходя страшные термины и не вспоминая исторических личностей.
Что такое логическое программирование
Этот яркий, но малоинформативный пример призван показать, что обычно команды выполняются одна за другой, причем мы ожидаем, что следующие инструкции (команды) могут использовать результат работы предыдущих. Также мы можем описывать собственные команды, код которых будет написан подобным образом. Остановимся на том, что как завещал Фон Нейман, так компьютер и работает. Машина глупа, она делает то, что скажет программист, по четкому алгоритму, последовательно выполняя инструкции. Не может же, в конце концов, компьютер, подобно мыслящему живому существу, делать выводы, строить ассоциативные ряды… Или может?
Давайте устроимся поудобнее рядом со своим компьютером и порассуждаем о жизни и смерти вместе с Аристотелем:
Всякий человек смертен.
Сократ - человек.
Следовательно, Сократ смертен.
Звучит логично. Но есть ли способ научить компьютер делать выводы как Аристотель? Конечно! И вот тут мы вспомним о Prolog-e, который так часто мелькает при поиске информации о логическом программировании. Как несложно догадаться, Prolog (Пролог) является самым популярным чисто логическим языком программирования. Давайте рассуждения об этом языке оставим на следующие разделы статьи, а пока что продемонстрируем "фишки" логических языков, используя Пролог.
Напишем небольшую программу, где перечислим, кто является людьми (ограничимся тремя) и добавим правило "всякий человек смертен":
Что ж, давайте спросим у компьютера, смертен ли Сократ:
Компьютер выдал нам сухое "true", но мы, конечно, вне себя от счастья, так как вот-вот получим премию за успешное прохождение нашим умным устройством теста Тьюринга.
Так, теперь стоит успокоиться и разобраться, что же произошло. Вначале мы записали т. н. факты, то есть знания нашей программы о мире. В нашем случае ей известно лишь то, что Платон, Сократ и Аристотель - люди. Но что за странная запись "human('Socrates')." и почему это выглядит как функция? На самом деле "human" и "mortal" - предикаты от одной переменной. Да, тут уже пошли термины, но постараюсь объяснять их просто и понятно для тех, кто привык к императивному нормальному программированию.
Логическое программирование основано на логике предикатов. Предикатом называется (и здесь я позволю себе вольность) функция от нуля или более переменных, возвращающая значение логического типа (истина (true) или ложь (false)). Факт - это предикат с определенным набором параметров и заведомо известным значением.
Помимо фактов в логической программе присутствуют правила вывода. В данном случае это "mortal(X) :- human(X).". Набор правил вывода - это знания нашей программы о том, как выводить (искать/подбирать) решение. Правила записываются следующим образом:
Предикат a от трех аргументов вернет истину, если удастся доказать истинность предикатов b, c и d. Читаются правила справа налево следующим образом: "Если b от X истинно И c от Y, Z истинно И d истинно, то a от X, Y, Z истинно".
Уже на таком небольшом примере видно, что мы не описываем четко последовательность действий, приводящую к нужному результату. Мы описываем необходимые условия, при выполнении которых результат будет достигнут. Тут будет к слову упомянуть, что раз компьютер сам для нас выводит способ достижения результата на основе известных правил, то и использовать один и тот же код можно по-разному:
Теперь начнём делать запросы к программе (всё те же предикаты):
Как видите, очень удобно. Стало быть, первым и очевидным применением логического программирования (об эффективности поговорим ниже) является работа с базами данных. Мы можем достаточно естественным образом описывать запросы, комбинируя предикаты, причем научить писать такие запросы можно даже человека, совершенно не знакомого с логическим программированием.
Какие задачи и как можно решать с помощью логического программирования
Давайте рассмотрим ряд учебных примеров (без подробного описания, все же статья обзорная) и подумаем, как те или иные подходы можно применять в реальной жизни. Начну с примера, призванного продемонстрировать, почему логическое программирование удобно, и за что же его любят математики. А именно, опишем правила вычисления производной:
Давайте на простом примере рассмотрим, что из себя представляет перебор, а затем то, чем он может быть опасен.
Спросим, кто из ребят, известных компьютеру - технический переводчик:
Ага…то есть Петя, Петя и ложь… Что-то не так, подумает программист и попробует разобраться. На самом деле, перебирая все варианты значений X, Пролог пройдёт по такому дереву:
Дерево будет обходиться в глубину, то есть сначала рассматривается всё левое поддерево для каждой вершины, затем правое. Таким образом, Пролог дважды докажет, что Петя - технический переводчик, но больше решений не найдёт и вернёт false. Стало быть, половина дерева нам, в общем-то, была не нужна. В данном случае, перебор не выглядит особенно страшным, всего-то обработали лишнюю запись в базе. Чтобы показать "опасность" перебора, рассмотрим другой пример:
Представим, что перед нами в ячейках расположены три чёрных и три белых шара (как на картинке выше), которые требуется поменять местами. За один ход шар может или передвинуться в соседнюю пустую клетку, или в пустую клетку за соседним шаром ("перепрыгнуть" его). Решать будем поиском в ширину в пространстве состояний (состоянием будем считать расположение шаров в ячейках). Суть этого метода заключается в том, что мы ищем все пути длины 1, затем все их продления, затем продления продлений и т. д., пока не найдем целевую вершину (состояние). Почему поиск в ширину? Он первым делом выведет самый оптимальный путь, то есть самый короткий. Как может выглядеть код решения:
Если вы попытаетесь вызвать предикат solve, то, в лучшем случае увидите ошибку, в худшем - среда зависнет. Дерево здесь (с учётом лежащих в памяти путей) настолько велико, что переполнит стек, так и не подарив нам ответа. Ну и что - скажете вы, это же может происходить и в императивных (процедурных (обычных)) языках программирования. Конечно. Но, повторюсь, на решение той же задачи на Питоне или Си (без использования библиотек) ушло бы на порядки больше времени. Давайте для полноты картины я приведу решение данной проблемы, а после перейдем к тому, какие же задачи решаются подобным образом.
Со стороны улучшения алгоритма можно предложить использовать поиск в глубину. Но как же, он ведь не даст оптимального результата? Сделаем просто: ограничим глубину поиска. Так мы точно не забьём стек и, возможно, получим ответ. Поступим так: проверим, есть ли пути длины 1, затем длины 2, затем длины 4 и т. д. Получим так называемый поиск с итерационным заглублением:
Во-первых, здесь стоит обратить внимание, что мы не используем очереди, а также внешних предикатов (кроме reverse, но он для красоты). Это потому, что поиск в глубину естественен для Пролога (ищите картинку с деревом выше). Во-вторых, пусть мы и делаем вроде как "лишние" действия, то есть для каждого нового значения глубины проходим по всем путям заново, мы практически не теряем в скорости относительно поиска в ширину (может в несколько раз, но не на порядок), при этом значительно экономим память. В-третьих, мы наконец-то получаем ответ, и это самое главное. Приводить его не буду, так как он займет много места, но для интриги оставлю вам его длину: 16.
С другой стороны, задачу можно было бы решить, не меняя код поиска, а лишь изменив правила перемещения шаров. Обратим внимание, что нам заранее известны входные и выходные данные. Приглядевшись становится понятно, что нет никакого смысла в движении фишек "назад". Действительно, если чёрным нужно встать в правые позиции, то какой смысл делать ходы влево? Перепишем предикаты движения:
Хм, код стал даже проще. Запустив мы убедимся, что поиск (оба варианта), во-первых, работает, во-вторых, работает быстро, в-третьих, работает быстро и выводит результат. Это успех. Мало того, что мы решили задачку, только что был создан самый настоящий искусственный интеллект. Программа получает входные данные и желаемый результат, а затем сама ищет, как его достигнуть. Да, это однозначно успех.
Зачем и где применяют логическое программирование
Давайте вернемся к рассмотренным примерам и попробуем представить, как это можно использовать на практике.
Анализ естественного языка: Пример с производной - классический пример разбора выражений. Но если мы заменим арифметические операторы, переменные и числа на слова, добавим правила, скажем, английского языка, то сможем получить программу, разбирающую текст на структурные элементы. Занимательно, что одновременно мы получим и программу, способную генерировать текст. Но если логическое программирование можно удобно и эффективно использовать для анализа и разбора текста, то в задачах генерации качественного текста скорее придется обращаться к нейросетям. Тут важно отметить, что рассуждая об анализе и генерации предложений нельзя не упомянуть сложность решения подобных задач. Человек при составлении и восприятии текста ориентируется не только на набор слов и их значений, но и на свою картину мира. К примеру, если в базе лежит факт "Миша починил Маше компьютер", то на вопрос "Разбирается ли Миша в компьютерах?" программа не сможет ответить, не смотря даже на то, что решение вроде как "на поверхности". Именно из-за низкой скорости и особенностей использования на чисто логических языках не занимаются тем, что мы ждем увидеть, загуглив "нейросети" (поиск котиков на картинке, например, не для Пролога). Но вот задачи синтаксического разбора, текстовой аналитики и т. п. на логических языках решать очень даже комфортно.
Поиск решений: Задача с Петей и Васей, а также задача с шарами - примеры поиска решений. Представим, что у нас есть набор знаний о некоторой системе и нам нужно понять, можно ли тем или иным путем её взломать (обойти защиту). Это можно достаточно лаконичным образом описать на логических языках и запустить процесс поиска решений. Помимо переборных задач, хорошо будут решаться те, где требуются логические рассуждения (анализ кода или, опять же, естественного текста).
Мета-программирование: С помощью того же Пролога можно описать свой собственный язык, соответственно, со своими правилами. Это достаточно важный момент для многих проектов, где сталкиваются специалисты разных сфер. Например, стоит задача анализа неких химических данных. В команде работают химик и программист. Получается, программист должен постоянно советоваться с химиком о том, что и как ему делать, ведь химическим формулам то в IT не учат. А разговаривать - это, в общем-то, не особенно приятно, особенно если кто-то умнее тебя. И тут программисту было бы очень удобно написать простой язык, на котором химик сможет сам записать свои формулы, не трогая разработчика. Вот тут и пригодится логическое программирование.
И тут крайне важно отметить, что решения на логических языках оказываются столь же неэффективны, сколько удобны (если речь не идёт о нишевых, специализированных решениях). Программа на императивном языке всегда обгонит аналогичную программу на логическом, но затраты на написание кода в ряде случаев (в том числе описанных выше) падают в разы. На практике вы вряд ли столкнетесь именно с Prolog-ом. Он, конечно, выразителен (можно описывать сложные вещи просто), хорошо расширяется, на нем легко писать надежный код и решать определенные задачи (в т. ч. просто логические задачки), но есть и ряд недостатков: пролог сильно уступает по скорости императивным языкам, а также не особенно поддерживается и не развивается, что делает его применение на практике практически невозможным.
Стоит ли планировать его изучение на 2021-й
Тут оставлю своё субъективное мнение, разделённое на две части:
Если вы только-только делаете первые шаги в программировании, то браться за логическое программирование не стоит, поскольку на практике вы будете практически всегда писать код, используя императивные языки, и их изучение - основа вашей будущей специальности. Учиться разрабатывать программы сразу и в императивной, и в декларативной (описываем задачу и результат, а не как его получить) парадигмах, как по мне, малоэффективно.
Если вы уже более-менее освоились в программировании, то браться за логическое программирование определенно стоит. Во-первых, учиться мыслить иначе - это замечательная тренировка для вашего мозга (а он, между прочим, является рабочим инструментом разработчика). Во-вторых, зачастую полезно задуматься, как вы решили бы задачу на том же Прологе, и посмотреть на код под другим углом. В-третьих, иногда крайне удобно сделать на логическом языке прототип, отображающий функционал, и затем разрабатывать полноценное решение. В-четвертых, пусть логические языки пригождаются редко в реальной практике, но вот декларативный подход встречается довольно часто, так как многие языки реализуют в себе логические или функциональные элементы.
И здесь остаётся лишь пожелать продуктивного 2021-го года!
Хочется выразить особую благодарность Дмитрию Сошникову за знакомство с этой удивительной парадигмой.
Читайте также: