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

Введение в логическое программирование (Prolog)

Заказать решение задачи на Prolog можно тут

На блоге я публиковал ряд статей по логическому программированию, а также разбирал решения задач на языке Prolog. Недавно я заметил, что из всего этого могла бы получиться полноценная методичка если добавить введение. Введение написано так, чтобы после его прочтения Вы смогли начать программировать на Prolog, более строгой с математической точки зрения материал стоит искать в книгах по ФЛП [2].

Первые языки логической парадигмы программирования были разработаны в середине 20 века как инструменты для автоматического доказательства теорем, при этом в их основе лежал математический аппарат исчисления предикатов [1]. Позже их стали применять в качестве языков программирования общего назначения, при этом языки были дополнены рядом синтаксических конструкций.

Языки логического программирования имеют небольшое распространение, тем не менее их применяют при разработке трансляторов и искусственного интеллекта [2], они могут использоваться для разработки любых десктопных и мобильных приложений [3, 4], а также сайтов [5]. Компания PDC, разрабатывающая Visual Prolog, применяет его при программировании авиационных и медицинских систем (конкретные проекты можно посмотреть на официальном сайте) [6]. Сам я разрабатываю на Prolog инструменты оптимизации программ — это оказалось гораздо удобнее чем на С++, при этом пользуюсь SWI Prolog (поэтому именно на нем написаны все примеры этой статьи).

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

Логическая программа состоит из предикатов, представляющими собой функции, вырабатывающие логические значения — любой предикат содержит вычисления, которые могут быть либо истинными, либо ложными. При этом результаты вычисления предикат возвращает только если вычисления истинны. Предикаты состоят из правил (предложений), описывающих вычисления и соединенных между собой логическими операторами И/ИЛИ, при этом логическому И соответствует оператор запятая, а ИЛИоператор точка. Программы на языке Prolog могут содержать также переменные (их имена обязательно должны начинаться с большой буквы — этим они отличаются от других объектов), однако одним из базовых в логическом и функциональном программировании является принцип однократного присваивания, в соответствии с которым переменная может получить значение лишь один раз, все остальные попытки присваивания будут работать как проверка на равенство. Следствием принципа однократного присваивания является отсутствия циклических конструкций в Prolog — вместо них везде применяется рекурсия, что не снижает скорость работы программы, т.к. хвостовая рекурсия также эффективна, как и цикл [9].

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

Язык программирования пролог примеры задач. Смотреть фото Язык программирования пролог примеры задач. Смотреть картинку Язык программирования пролог примеры задач. Картинка про Язык программирования пролог примеры задач. Фото Язык программирования пролог примеры задач дерево вычислений prolog (вычисление суммы цифр числа)

Подобное дерево строится во многих интерпретаторах языка Prolog, т.к. выбор операторов для выполнения в программе должен выполняться в порядке обхода дерева слева направо. Розовым цветом я обозначил предикат, состоящий из двух правил, выделенных зеленым цветом. Листья дерева соответствуют операциям, вычисление которых может завершится либо истиной, либо ложью. В операциях, соединенных логическим И все операции должны вернуть истинное значение чтобы результатом стала истина, поэтому если любая из таких операций завершилась ложью — результат может быть получен сразу (без вычисления остальных). Так например, если Number >= 10, то первое правило сразу завершится ложью и управление будет передано второму правилу, т.к. они соединены с помощью логического ИЛИ.

Чуть позже мы еще раз вернемся к примеру с суммой цифр числа, но сначала рассмотрим механизмы поиска с возвратами ( backtracking ) и сопоставления с образцом ( pattern matching ), которые также лежат в основе логического программирования. Рассмотрим их на примере программы вычисления стоимости покраски плоской поверхности.

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

В данном случае программа состоит из двух предикатов:

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

В приведенном фрагменте исходного кода предикат colour_cost состоит из трех правил, в качестве аргументов правила используются константы (например red и 1000), значения которых интерпретатор пытается присвоить передаваемым переменным или выполнить сравнение. Так, например, при обработке colour_cost(Color, 1070) выбирается сначала самый левый узел дерева, поэтому переменной Color присваивается значение red, а 1070 сравнивается с 1000 — сравнение возвращает false поэтому результат работы не возвращается и интерпретатор пытается подставить следующий узел дерева (colour_cost(white, 1030)). Такой механизм называется сопоставлением с образцом.

Кроме того видно, что при интерпретации предиката пролог может возвращать несколько вариантов решений — это является следствием работы механизма поиска с возвратами, заключающегося в полном переборе вариантов решений. При поиске интерпретатор обходит узлы дерева слева направо, если какая либо ветвь завершилась успешно (вернула истину) — результат возвращается, после чего поиск по дереву может быть продолжен для получения других вариантов решения. Так например, при интерпретации цели всех вариантов покраски прямоугольника 10 на 5 метров с ценой менее 52000 вызывается colour_cost, который в свою очередь вызывает colour_cost(Colour, ColourCost), а значит инициирует полный перебор вариантов покраски. Для каждого варианта будет рассчитана цена, которая после выхода из функции colour_cost сравнивается с константной 52000. Если сравнение проваливается (оператор возвращает false), интерпретатор пытается найти другое решение путем возврата внутрь colouring_cost, а затем подставит следующий необработанный цвет и его стоимость. Если решение для цели найдено — результат возвращается, однако поиск все равно может быть продолжен.

На механизм поиска с возвратам, а следовательно и на управление вычислениями (порядок выполнения операторов) может влиять оператор отсечения, обозначаемое в языке Prolog восклицательным знаком (вы могли видеть его использование в примере функции вычисления суммы цифр числа). При выполнении оператора отсечения перебор решений ограничивается так, что значения всех узлов, выполненных до отсечения (находящихся в дереве «левее») считаются константами — запрещается возврат к ним для расчета других значений. Мы уже использовали отсечение при вычислении суммы цифр числа — если число меньше десяти, то оно содержит лишь одну цифру, а значит нет смысла в рекурсивной обработке числа, т.е. переходе ко второму правилу. В связи с этим первое правило вычисления суммы содержит отсечение. Другие примеры использования отсечения [8]. Различают красные и зеленые отсечения [10].

Важной особенностью логических языков программирования является наличие встроенной базы данных. В зависимости от диалекта, встроенная БД может хранить либо только факты (предикаты, правила которых не содержат тело — как рассмотренный выше предикат colour_cost), либо произвольные предикаты. Работа с записями базы данных в Prolog происходит точно также, как и с любыми другими предикатами, однако их можно добавлять и удалять во время выполнения встроенными функциями assert и retract, чтобы это стало возможным в Vusial Prolog факты требуется описать в секции database, а в SWI Prolog — описать динамическими [11]:

Тогда добавление записи в базу может быть выполнено командой assert(colour_cost(black, 900)), а удаление — retract(colour_cost(red, Cost)). В остальном работа с ними не изменится. В силу того, что записи базы данных пролога в плане обработки никак не отличаются от других предикатов (обрабатываются в соответствии с механизмом поиска с возвратами) — встроенная база данных имеет существенные отличия от баз данных SQL [12].

Принцип однократного присваивания не позволит изменить значение элемента массива, поэтому базовой структурой данных в логических языках программирования является связный список. При этом изменение элемента списка может реализовываться через удаление старого узла и добавление нового. При обработке списков на Prolog учитывается их рекурсивная природа — любой список из одного или более элемента возможно разделить на первый элемент и другой список, особым видом списка является пустой список. Список может быть задан перечислением элементов в квадратных скобках — [1, 2, 3], для обозначения пустого списка используется конструкция из двух квадратных скобок — []. Основной операцией по работе со списками является разделение списка на голову (первый элемент) и хвост (список из остальных элементов) — обозначается вертикальной чертой: [Head|Tail] = [1, 2, 3] — в результате Head = 1, Tail = [2, 3]. Обработка списка происходит рекурсивно, при этом от исходного списка отделяются первые элементы до тех пор, пока он не станет пустым (такой случай обрабатывается отдельно). Примеры рекурсивной обработки списков [13]. Списки являются тем более важными, что с их помощью в Prolog задаются строки, даже в тех диалектах, где строки не являются списком (например Turbo/Visual Prolog), для обработки их удобно преобразовать в список, т.к. это позволит применять к ним более богатый набор функций [14].

Заключение

Статья призвана объединить и сгруппировать информацию по логическому программированию, размещенную на этом блоге — в связи с этим в ней описано небольшое число примеров, но содержится много ссылок, где такие примеры можно найти. Статья является вводной, поэтому в ней разобраны лишь самые основы языка, но любой желающий читатель без труда найдет больше информации, например по работе с файлами [15]. Кроме того, статья ориентированна на начинающих, я хотел, чтобы после ее прочтения (и беглого просмотра связанных ссылок) неподготовленные читатели смогли писать простые простые программы на языке Prolog — поэтому она не содержит строго математического описания логических языков программирования (которое при желании можно найти в книгах) [2].

Источник

Язык Prolog урок 5. Метод отсечения

Метод отсечения

Отсечение может «работать» в двух режимах:

Рассмотрим пример использования метода:

domains a=symbol predicates child(a) make_cut(a) clauses child(sveta). child(diana). child(ivan). child(diana). child(igor). make_cut(X):-child(X),X=diana, write(» «,X). goal make_cut.

domains a=symbol predicates child(a) make_cut(a) show_some clauses child(sveta). child(diana). child(ivan). child(diana). child(igor). make_cut(X):-child(X),X=diana. show_some :- child(Name), write(» «, Name), nl, make_cut(Name). goal show_some_of_them.

Примечание:
Следует обратить внимание на то, что в данной задаче в правиле в качестве аргументов имеющихся фактов указанны переменные:
так, в правиле покупка_автомобиля(Модель,Цвет) — слова Модель и Цвет — это переменные.

Запросы:

Отрицание

Обычно в таком случае используемые объекты имеют некоторое число общих атрибутов.

Рассмотрим пример и выполним задание.

Выполнить задание до конца, реализовав все факты и правило.

Составные объекты

В данном примере аргументом отношения (предиката) likes служит другой предикат fruits.

Звучит это так: функтор структуры personal_liking имеет имя fruits. Предикат likes использует эту структуру.

    Запросы:

domains personal_library = book(title,author,publisher,year) collector,title,author,publisher = symbol year = integer predicates collection(collector,personal_library) /* коллекция (имя коллекционера, библиотека) */ clauses collection(kahn, book(«The Computer and the Brain», «von Neumann», «Yale University Press», 1958)). collection(kahn, book(«Symbolic Logic», «Lewis Carroll», «Dower Publications», 1958)). collection(johnson, book(«Database: A Primer», «C.J.Date», «Addison-Wesley», 1983)). collection(smith, book(«Alice in Wonderland», «Lewis Carroll», «The New American Library», 1960)).

Решение логических задач на языке Пролог

domains a=symbol predicates имя(a) место(a) соответствие(a,a) clauses имя(алеша). имя(петя). имя(коля). место(первое). место(второе). место(третье). соответствие(X,Y):-имя(X),место(Y),X=петя, not(. ),not(. ). соответствие(X,Y):-?. соответствие(X,Y):-?.

domains a=symbol predicates справа(a,a). ряд(a,a,a). крайний_справа(a). посередине(a). крайний_слева(a). clauses справа(коля,боря). справа(боря,петя). ряд(X,Y,Z):-справа(. ),справа(. ). крайний_справа(X):-. посередине(X):-. крайний_слева(X):-.

domains a=string predicates выше(a,a) самое_высокое(a) самое_низкое(a) ряд(a,a,a,a,a,a) clauses выше(. ). выше(. ). выше(. ). выше(. ). выше(. ). ряд(A,B,C,D,F,G):-. самое_высокое(X):-. самое_низкое(X):-.

Ответ: Ряд: A=репка, B=дедка, C=бабка, D=внучка, E=жучка, F=кошка, G=мышка.

Источник

Решение логических задач на Prolog

Заказать решение задачи на Prolog можно тут

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

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

Более подробно про историю языка Пролог можно почитать в толстых книгах [1, 2, 3], но а я привел эти факты чтобы чуть-чуть отразить особенности развития этого языка и решаемых на нем задач. Традиционно в российских ВУЗах на предметах «логическое и функциональное программирование», «искусственный интеллект» выдаются задания связанные с решением логических задач. Ноги у этих задач растут из автоматического доказательства теорем.

В статье рассматривается 2 типа логических задач — задачи на установление соответствия и задачи поиска в пространстве состояний.

Логические задачи

Задачи на установление соответствия

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

Примером такой головоломки может быть задача про купе:

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

Пассажир купе будет описываться фактом passenger:

passenger(Name, Read, Buy, Write).

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

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

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

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

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

Рассмотрим еще одну задачу на поиск соответствия:

Три друга заняли первое, второе и третье места в соревнованиях универсиады. Друзья — разной национальности, зовут их по-разному и любят они разные виды спорта.

Майкл предпочитает баскетбол и играет лучше чем американец. Израильтянин Саймон играет лучше теннисиста. Игрок в крикет занял первое место.

Кто является австралийцем? Каким видом спорта занимается Ричард?

Решение задачи опять начинается с формализации фактов. Пролог не знает что теннис — это спорт, а Саймон — имя. Сообщим ему об этом:

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

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

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

Задачи поиска в пространстве состояний

Во многих логических задачах заданы начальное и конечное условия (состояния), и какие-либо правила поведения. Требуется найти последовательность действий, которая переведет из начального состояния в конечное. Условно, такие задачи можно разделить на 3 типа, которые мы рассмотрим на примерах.

Фиксированное число состояний

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

В качестве примера рассмотрим следующую задачу:

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

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

Собственно, но этом решение закончилось, осталось написать 3 запроса (по запросу на каждый пункт задания):

Результаты выполнения запросов приведены на рисунках 3-5.

Язык программирования пролог примеры задач. Смотреть фото Язык программирования пролог примеры задач. Смотреть картинку Язык программирования пролог примеры задач. Картинка про Язык программирования пролог примеры задач. Фото Язык программирования пролог примеры задач рис. 3 писк всех путей в лабиринте Язык программирования пролог примеры задач. Смотреть фото Язык программирования пролог примеры задач. Смотреть картинку Язык программирования пролог примеры задач. Картинка про Язык программирования пролог примеры задач. Фото Язык программирования пролог примеры задач рис. 4 поиск путей через золотую комнату Язык программирования пролог примеры задач. Смотреть фото Язык программирования пролог примеры задач. Смотреть картинку Язык программирования пролог примеры задач. Картинка про Язык программирования пролог примеры задач. Фото Язык программирования пролог примеры задач рис. 5 поиск путей без запрещенных комнат

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

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

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

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

Схематично, начало процесса работы программы показано на рис. 6. Состояния изображены цветными овалами, возможность перехода отражают дуги. Зеленым цветом выделены «правильные» состояния, которые обрабатывается программой, красным — «неверные» состояния (в которых коза съест капусту или волк — козу), желтым — уже обработанные состояния. Программа должна рассматривать все состояния, выполнять проверку и добавлять в граф только зеленые.

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

Для генерации новых состояний будем использовать правило permutation, которое выдаст нам все варианты подсписков заданной величины. В этой задаче, предел длины — 1, т.к. в лодке одно место. Когда подсписок будет получен, его нужно будет отнять от одного берега (правило subtraction) и прибавить к другому (стандартный предикат append).

Проверка корректности списка (правило check) и генерация новых состояний (правило generate) могут быть выражены следующим образом:

Разберем по строчкам правило генерации из состояния фермера на левом берегу:

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

Видно, что в обычный обход графа в ширину, описанный ранее, была добавлена лишь третья строчка. Перед тем, как искать смежные вершины (вызов findall в 4 строке) надо их сгененировать, чем и занимается наш generate.

Запрос для машины логического вывода и решение, найденное прологом, приведены на рис. 7.

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

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

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

Если мы попытались бы решить эту задачу также как предыдущую — нас ждала бы неудача, т.к. даже на первом шаге есть 120 вариантов посадить в лодку 3 человека, а можно ведь заполнить лодку не полностью (т.е. в дереве, подобном тому, что приведено на рис. 6 из начальной вершины выходило бы более 100 дуг). И это только на первом шаге. Очевидно, поиск в ширину (по крайней мере, обычный) не сработает в этой ситуации. Задачу можно решить поиском в глубину.

Для начала, опишем пары и правила проверки списка на правильность (пары в списке не должны быть разрушены):

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

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

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

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

Язык программирования пролог примеры задач. Смотреть фото Язык программирования пролог примеры задач. Смотреть картинку Язык программирования пролог примеры задач. Картинка про Язык программирования пролог примеры задач. Фото Язык программирования пролог примеры задач рис. 8 решение логической задачи о супружеских парах

Решение логических задач — обширная тема, по которой написаны толстые (и даже современные) книги [7,8], которые явно стоит посмотреть всем небезразличным. Безразличные тоже могут почитать для расширения кругозора.

Решение задач методом генерации гипотезы

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

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

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

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

Модель предметной области

Для начала — опишем типы данных:

Типы данных списков ( colors, distances, instruments ) нужны нам дальше для рганизации перебора.

Также мы можем объявить константы (в разделе constants ) чтобы фиксировать возможные цвета, инструменты и расстояния. Эти константы нужны нам для перебора вариантов — нам нужно будет чтобы программа выбирала один из возможных цветов (элементов списка all_colors ):

Перебор вариантов

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

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

Для перебора возвожных вариантов используется функция member:

Рассмотрим как происходит генерация возможных цветов. Сначала для каждого человека получим свой цвет (элемент списка all_colors ):

А затем, проверим что цвета не повторяются, ведь в условии сказано «Они владеют разными музыкальными инструментами и выступают в костюмах разных цветов»:

Проверка условий

Первое правило «Джон играет на саксофоне и находится ближе к дирижёру, чем тот, кто выступает в белом костюме» можно описать так:

То есть мы просим программу найти в оркестре Джона, да такого, что он играет на саксофоне. Часть неверных решений (где Джон не играет на саксофоне) отсеится уже на этом этапе. В итоге мы получили структуру Джона — нас в ней интересует (в контексте этого правила) расстояние от Джона до дирижера ( DzhonDist ). А вот цвет костюма Джона нас не интересует — поэтому первым символом имени переменной _DzhonColor является подчеркивание (без подчеркивания) компилятор сообщал бы нам о возможной ошибке — что переменная не используется.

Дальше, мы аналогичным образом находим в оркестре человека, носящего белый костюм и в конце концов, проверяем что правильность расстояний (DzhonDist nondeterm solution(solution)

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

Источник

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

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