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

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

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

Пролог - уникален по своей природе, он появился благодаря счастливому совпадению (таинственному устройству мира). Когда-то в 60-х годах очень бурно развивалась теория автоматического доказательства теорем и Робинсоном был предложен алгоритм резолюций, который позволял доказать любую верную теорему (вывести из аксиом) за конечное время (за какое не известно). Как оказалось позже, это наилучшее решение общей задачи, невозможно доказать теорему за ограниченное число операций. Простыми словами, алгоритм представляет собой обход (в общем случае бесконечного) графа в ширину, естественно, что предсказуемость работы алгоритма практически равно 0, соответственно для Языка Программирования - это абсолютно не подходит. И в этот момент Кальмэроу нашел блестящее сужение задачи, благодаря которому доказательство некоторых теорем выглядело как процедурное исполнение программы. Стоит отметить, что класс доказуемых теорем достаточно широк и очень хорошо применим для класса программируемых задач. Вот так в 1972 появился Prolog.

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


Главной чертой Prolog является то, что его можно легко читать, но очень тяжело писать, что принципиально отличается от всех mainstream языков, которые так и говорят писать стало еще легче еще один шаг и можно будет писать на планшете, перетягивая рабочие модули как друзей в Google+ , от этого все мы знаем очень сильно страдает само качество кода. Вроде бы каждая строчка понятна, но как система работает за гранью понимания даже для разработчиков, как говорится наиндусили. Мне кажется во всех книгах по обучению Prolog, делают одну и ту же ошибку, начиная рассказ о фактах, отношениях, запросах и у человека складывается отношение к языку как к Экспертной Системе или Базе Данных. Гораздо важнее научится правильно читать программы и почитать так с десяток:)

Как правильно читать программы на прологе

Читать программы очень просто, так как в языке очень мало специальных символов и ключевых слов и они легко переводятся на естественный язык. Главная ошибка программиста, что он хочет сразу представить как программа работает, а не прочитать, что программа описывает, поэтому мне кажется обучить незатуманенный мозг обычного человека, гораздо проще чем програмиста.
Понятия
В языке существует 2 понятия предикаты (условия) и объекты (они же переменные и термы). Предикаты выражают некоторое условие, например объект зеленый или число простое, естественно что условия имеют входные параметры. Например green_object(Object) , prime_number(Number) . Сколько в предикате параметров, такова и арность предиката. Объектами - являются термы, константы и переменные. Константы - это числа и строки, переменные - выражают неизвестный объект, возможно искомый, и обозначаются как строчки с большой буквы. Оставим пока термы и рассмотрим простейшую программу.
Программа
Программа - это набор правил, вида Если условие1 и условие2 и… то верно условие. Формально эти правила объединяются через И, но противоречие получить невозможно, так как в Прологе отсутствует логическое отрицание, а в связке То может присутствовать только один предикат (условие).

A:- B_1, B_2. % правило читается как: Если B_1 и B_2, то A
нечетное_простое(Число) :- простое(Число), нечетное(Число).
% Если "Число" - простое и нечетное, то "Число" - нечетное_простое

Как видно имя переменной имеет область видимости - это правило. Математически верно, правило звучит: для любой переменной - «Число», если оно простое и нечетное, то оно простое_нечетное. Аналогично, можно перефразировать так: Если существует «Число», что оно нечетное и простое, то оно нечетно_простое. Поэтому имя переменной очень важно! Если в левой части (до:-) заменить Число на Число2, то правило поменяет смысл: Для любого Число2 и Число, если Число - простое и нечетное, то Число2 - простое нечетное. Получается все числа простые_нечетные! Это самая распространенная ошибка в Прологе.

A:- B_1, B_2. % правило читается как: Если B_1 и B_2, то A нечетное_простое(Число) :- простое(Число), нечетное(Число). % Если "Число" - простое и нечетное, то "Число" - нечетное_простое

Пример - совершенные числа
совершенное_число(Ч) :- число(Ч), сумма_делителей_без_числа(Ч, СуммаДелителей), равно(СуммаДелителей, Ч). совершенное_число(1). равно(Объект, Объект). сумма_делителей_без_числа(1, 1). сумма_делителей_без_числа(Число, Сумма) :- число_предыдущее(Число, Предыдущее), сумма_делителей_числа_до_числа(Число, Сумма, Предыдущее). сумма_делителей_числа_до_числа(Число, 1, 1). сумма_делителей_числа_до_числа(Число, Сумма, Делитель) :- делится_на(Число, Делитель), число_предыдущее(Делитель, Предыдущее), сумма_делителей_числа_до_числа(Число, СуммаПред, Предыдущее), сложить(СуммаПред, Делитель, Сумма). сумма_делителей_числа_до_числа(Число, Сумма, Делитель) :- не_делится_на(Число, Делитель), число_предыдущее(Делитель, Предыдущее), сумма_делителей_числа_до_числа(Число, Сумма, Предыдущее).

Для начала формально прочитаем, что означают правила:

  1. Если «Ч» - число и для «Ч» и «СуммаДелителей» выполняется условие сумма_делителей_без_числа, проще говоря СуммаДелителей есть сумма делителей числа «Ч», и «Ч» равно «СуммаДелителей», то «Ч» совершенное число.
  2. 1 - совершенное число. Правила могут не иметь условий, в этом случае они называются фактами.
  3. Всякий объект «О» равен «О». В принципе существует, стандартный предикат "=", но можно вполне заменить на свой.
  4. Факт сумма_делителей_без_числа 1 равна 1.
  5. Если сумма делителей «Число» до предыдущего числа «Число» равна «Сумма», то это и есть сумма_делителей_без_числа. Таким образом выражается, сумма делителей X меньше либо равных Y, так как X делится на X, поэтому берем Y = X - 1.
  6. Далее 3 предиката определяют сумму делителей число меньше либо равных Y (Делитель), 1-й случай Y равное 1, 2-й случай Число делится на Y, тогда сумма_делителей(X, Y) = сумма_делителей(X, Y-1) + Y, и 3-й случай Число не делится на Y, тогда сумма_делителей(X, Y) = сумма_делителей(X, Y-1).
Программа - как набор определений
Существует второй способ прочтения данных правил, менее математический и более естественный, основанный на «определениях». Можно заметить, что в Прологе все правила слева (в части то) содержат только одно условие, что по сути является «определением» это условия.
Например, 1-ое правило определение совершенных чисел. «Ч» совершенное число, когда «Ч» число и сумма делителей «Ч» равна «Ч». Одинаковые предикаты группируются по имени объединяясь условием «или». То есть к определению можно добавить: «Ч» совершенное число, когда.., или когда «Ч» - это 1.

Данный способ чтения широко применяется, так как позволяет объединять предикаты в однородные группы и помогает понять, в каком же порядке интерпретатор раскручивает предикаты, для того, чтобы
проверить истинность некоторого утверждения. Например, очевидно, что если предикат не имеет ни одного определения, то доказать истинность утверждения с ним невозможно. В примере № 1 не имеет определения предикат «делится_на».

Интересный факт, что в Прологе нет ни циклов, ни присвоения переменных, ни объявления типов, а если вспомнить еще про термы и отсечение, то язык становится алгоритмически полным.

Термы
Термы имеют рекурсивное определение, как именованная совокупность объектов. Терм = "имя"(объект, объект, ...), пример person("Name", "Surname"), "+"(1, 2), person(address("Некоторый адрес"), surname("Фамилия"), phone("Телефон")) . Если рассматривать терм, как математическое понятие, то терм является функцией, а точнее функтором, то есть "+"(1, 2) - означает, что существует такой объект, который равен 1+2. Это абсолютно не означает, что 1+2 = 3, в Прологе - это выражение неистинно, точно так же как и в группе остатков по модулю 2, там 3 вообще не существует. Опять же с математической точки зрения Переменные связываются словом Для Всех, а если в утверждении необходимо слово существует то, для этой цели применяется терм (функтор). Для любого числа существует число-факториал:- factorial(X, fact(X)).

С точки зрения программирования терм можно объяснить гораздо проще: терм - это объект с набором атрибутов, атрибуты могут быть другими термами или константами или переменными (то есть не определены). Главное отличие, все объекты в Prolog immutable, то есть менять атрибуты в них нельзя, зато есть специальное состояние - переменная.

Пример - целочисленная арифметика
нат(0). нат(число(Число)) :- нат(Число). плюс(0, Число, Число). плюс(число(Ч1), Ч2, число(Рез)) :- плюс(Ч1, Ч2, Рез). умножить(0, Число, 0). умножить(число(Ч1), Ч2, Рез2) :- умножить(Ч1, Ч2, Рез), плюс(Рез, Ч2, Рез2).
  1. Определение свойства нат (натуральное число). 0 - натуральное число, если Число натуральное, то существует объект число(Число), которое тоже является натуральным. Математически терм «число» выражает функцию +1, с точки зрения программирования «число» рекурсивная структура данных, вот ее элементы: число(0), число(число(0)), число(число(число(0))).
  2. Отношение плюс - 0 + Число = Число. Если Ч1 + Ч2 = Рез, то (Ч1+1) + Ч2 = (Рез+1).
  3. Отношение умножить - 0 * Число = 0. Если Ч1 * Ч2 = Рез и Рез + Ч2 = Рез2, то (Ч1+1) * Ч2 = Рез2.
Очевидно эти утверждения верны для обычной арифметики, но почему тогда мы не включили такие же очевидные как Число + 0 = Число. Ответ простой: избыточность очень плохо для любого определения. Да, это может помогать вычислениям, своеобразная преждевременная оптимизация, но побочными эффектами могут быть противоречия в определениях, неоднозначный вывод утверждения, зацикливание интерпретатора.

Как Prolog понимает предикаты и как доказывает утверждения

Конечно чтение программ, помогает ощутить стиль Пролог, но не делает понятным для чего и как данные определения могут использоваться. Полноценной программой, примеры приведенные выше, назвать нельзя так как не хватает входной точки. Входной точкой в Пролог является запрос, аналог запроса к базе данных SQL или аналог вызова главной функции в функциональном программировании. Примеры запросов: нат(Число) - найти натуральное число, плюс(0, 0, Результат) - найти результат сложения 0 и 0 в переменной Результат, нат(0) - проверить является ли 0 натуральным числом и др.

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

Рассмотрим на примере запроса плюс(0, 0, Результат) :
1. Находим совпадение (своеобразный pattern-matching, резолюция) данного запроса с левой частью одно из правил. Для данного запроса плюс(0, Число, Число). Соотнесем поочередно все аргументы запроса с правилом и получим: 0 = 0, 0 = Число, Результат = Число. В этих уравнениях участвуют 2 переменные (Число и Результат), решив их мы получаем, что Число = Результат = 0. Так как у данного правила нет условий, мы получили ответ на заданный вопрос. Ответ: да и Результат = 0.

Запрос нат(Число) :
1. Находим 1-е совпадение с правилом, правило нат(0), решая уравнения по соответствию, проще говоря находя резолюцию, мы получаем Число = 0. Ответ: да и Число = 0.

Запрос плюс(Результат, 0, число(0)) :
1. Находим резолюцию с правилом плюс(0, Число, Число): Результат = 0, 0 = Число, число(0) = Число, но (!) Число = 0 = число(0) - не возможно так как 0 совпадает число(0). Следовательно ищем резолюцию со следующим правилом.
2. Находим резолюцию с правилом плюс(число(Ч1), Ч2, число(Рез)), получаем число(Ч1) = Результат, Ч2 = 0, число(Рез) = число(0), отсюда Рез = 0. У этого правила, есть условия которые мы должны проверить, учитывая результаты резолюции (значения переменных), плюс(Ч1, Ч2, Рез) -> плюс(Ч1, 0, 0). Запоминаем значение переменных в стеке и формируем новый запрос плюс(Ч1, 0, 0)
3*. Решая запрос плюс(Ч1, 0, 0) находим резолюцию с плюс(0, Число, Число) и получаем Ч1 = 0 и Число = 0.
4. Возвращаемся по стеку к предыдущим переменным Результат = число(Ч1) = число(0). Ответ найден число(0). Соответственно сейчас пролог машина решила уравнение X + 0 = 1.

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

Пример запроса плюс(Число, Число, Число) : ответ да, Число = 0.

Пример запроса плюс(0, 0, 0) : ответ нет, при первой же попытке все резолюции не выполняются.

Пример запроса плюс(Число, Число, число(Число)) : ответ да, Число = 1. Решение уравнения X + X = X + 1.

Попробуйте провести вывод для умножить(Число, число(0), число(0)), для этого потребуется 2 раза заносить в стек переменные и вычислять новый запрос. Суть Пролог машины такова, что вы можете отказаться от 1-го результата, тогда Пролог вернется к предыдущему состоянию и продолжит вычисление. Например запрос нат(Число) , сначала применит 1-е правило и выдаст 0, а затем применит 2-е правило + 1-е правило и выдаст число(0), можно повторить и получить бесконечную последовательность всех натуральных чисел. Другой пример, запрос плюс(Число, число(0), Число2) , будет выдавать последовательность всех пар решения уравнения X + 1 = Y.

Заключение

К сожалению, разумный размер топика, не дал мне подобраться к главной теме, а именно к решению сложных логических задач на языке Пролог, не обладая стратегией их решения. Большие куски кода на Прологе могут отпугнуть не только начинающих, но даже опытных программистов. Цель данной статьи показать, что программы на Прологе могут простым образом читаться на естественном языке , а также исполняться простейшим интерпретатором .
Главная особенность Пролога - это не черный ящик и не библиотека, который решает сложные логические задачи, в Mathematica можно ввести алгебраическое уравнение и она выдаст решение, но последовательность выполняемых шагов - неизвестна. Пролог не может решать общие логические задачи (у него отсутствует логическое «или» и «отрицание»), иначе бы его вывод был недетерминированный как линейной резолюции. Пролог - это золотая середина, между простым интерпретатором и машиной для доказательства теорем, сдвиг в любую сторон приводит к потери одного из свойств.

В следующей статье я бы хотел рассказать, как решаются задачи сортировки, о последовательности переливаний, Miss Manners и другие известные логические задачи. Для тех, кто почувствовал себя неудовлеторенным хочу предложить следующую задачу (решившему первым приз ):
Написать предикат , который бы генерировал, бесконечную последовательность натуральных чисел, начиная с 3. Это должны быть стандартные числа в Прологе, операции над которыми выполняются при помощи предиката is: X is 3 + 1 => X=4.

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

Идея использования логики исчисления предикатов I порядка в качестве основы языка программирования возникла в 60-е годы, когда создавались многочисленные системы автоматического доказательства теорем и вопросно-ответные системы. В 1965 г. Робинсон предложил принцип резолюции, который в настоящее время лежит в основе большинства систем поиска логического вывода. Метод резолюций был использован в системе GPS (general problem solver). В нашей стране была разработана система ПРИЗ, которая может доказать любую теорему из школьного учебника геометрии.

Язык программирования PROLOG (programming in logic) был разработан и впервые реализован в 1972 г. группой сотрудников Марсельского университета во главе с Колмероэ. Группа занималась проблемой автоматического перевода с одного языка на другой. Основа этого языка - исчисления предикатов I порядка и метод резолюций.

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

Суть Пролога – программирование в терминах целей. Программист описывает условие задачи, пользуясь понятиями объектов различных типов и отношений между ними, и формулирует вопрос. PROLOG-система обеспечивает ответ на вопрос, находя автоматически последовательность вычисления решения, используя встроенную процедуру поиска. До 1981 г. число исследователей, занимавшихся логическим программированием, составляло около сотни во всем мире. В 1981 году PROLOG был выбран в качестве базового языка компьютеров пятого поколения, и количество исследователей логического программирования резко возросло. Одной из наиболее интересных тем исследований является связь логического программирования с параллелизмом.

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

В настоящее время создано достаточно много реализаций языка Пролог: Wisdom Prolog, SWI Prolog, Turbo Prolog, Visual Prolog, Arity Prolog и т.д.

В нашем курсе будем использовать SWI Prolog. SWI-Prolog развивается с 1987 года. Его создателем и основным разработчиком является Ян Вьелемакер (Jan Wielemaker). Название SWI происходит от Sociaal-Wetenschappelijke Informatica (гол. социально-научная информатика), первоначального названия группы в Амстердамском университете, где работает Вьелемакер.

SWI-Prolog позволяет разрабатывать приложения любой направленности, включая Web-приложения и параллельные вычисления, но основным направлением использования является разработка экспертных систем, программ обработки естественного языка, обучающих программ, интеллектуальных игр и т.п. Это интерпретатор. Файлы, содержащие программы, написанные на языке SWI Prolog, имеют расширение pl.

SWI-Prolog-Editor является средой программирования для языка SWI-Prolog, включающую редактор программ с подсветкой синтаксиса, интерпретатор и отладчик программ. Основным назначением среды является обучение логическому программированию на языке Prolog.

Сначала устанавливаем SWI Prolog, затем - SWI Prolog Editor. Для запуска редактора SWI Prolog Editor необходимо запустить файл SwiplEdit.exe. Для настройки работы интерпретатора в специальном окне редактора, следует установить путь к интерпретатору, выполнив в редакторе команду Окно-Конфигурация на закладке Программы установить в строке Папка Пролога путь к интерпретатору. Там же, на закладке Настройки необходимо установить поле Codepage равным cp1251. Настройка кодовой страницы необходима для правильного сопоставления строковых констант, набранных русским алфавитом, между текстом программы в среде SWI-Prolog-Editor и языком SWI-Prolog. Для запуска программы из панели редактирования программ ее следует сохранить и нажать функциональную клавишу F9 или соответствующий значок на панели инструментов. В случае успешной загрузки на панели запросов появится:

Consult(<имя файла>).

Загрузить файл можно так же с помощью команды: [<имя файла>].

После любой модификации программу требуется заново загрузить в память. Перезапуск интерпретатора Пролога осуществляется нажатием Ctrl+F9 или соответствующего значка на панели инструментов.

Для выхода из интерпретатора Пролога используется команда: halt.

Факты и правила

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

Рисунок 10 - Дерево родственных отношений

Пример 1: Это дерево можно описать следующей Пролог-программой.

родитель(пам, боб).

родитель(том, боб).

родитель(том, лиз).

родитель(боб, энн).

родитель(боб, пат).

родитель(пат, джим).

У нас имеется отношение родитель между двумя объектами. Описаны 6 фактов наличия отношений между конкретными объектами. Имена объектов начинаются с маленьких букв (они являются константами). В Прологе принято соглашение, что константы начинаются с маленькой буквы, а переменные – с большой. После набора такой Пролог-программы в редакторе можно загрузить программу в Пролог и задавать вопросы, касающиеся отношения родитель.

Запрос к программе набирается после приглашения?- и должен заканчиваться точкой. Для выполнения набранного запроса необходимо нажать Enter. Ответ будет выдан под запросом. Запрос может быть набран в несколько строк - для перехода на новую строку используется клавиша Enter. В том случае, если строка будет заканчиваться точкой и будет нажата клавиша Enter SWI-Prolog начнет выполнение запроса. Если возможны несколько вариантов ответа на запрос, то для получения каждого следующего используется клавиша Enter. Варианты ответов SWI-Prolog отделяет друг от друга точкой с запятой. Прекратить выполнение программы (выдачу альтернативных ответов) можно нажав клавишу «a».

Вопросы могут быть простые и сложные (в качестве связки «и» при составлении сложного вопроса используется запятая). Ответы Пролог-системы выводятся сразу после вопроса. Могут быть следующие варианты ответов:

  • No (соответствует нет или не найдены значения переменных в вопросе);

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

Вопрос относительно отношения родитель

Вопрос в Пролог-системе

Ответ Пролог-системы

Боб является родителем Пат?

родитель(боб,пат).

Пат – ребенок Лиз?

родитель(лиз,пат).

Кто родители Лиз?

родитель(X,лиз).

Кто дети Энн?

родитель(энн, X).

Кто дети Боба?

родитель(боб,X).

Есть ли дети у Лиз?

родитель(лиз,_).

Кто чей родитель?

родитель(X,Y).

Кто внуки Тома?

родитель(том,X),

родитель(X,Y).

Кто родители родителей Джима?

родитель(X,джим),

родитель(Y,X).

Итак, в простейшем случае Пролог-программа описывает факты и правила.

Факт – это безусловное утверждение (всегда истинное), характеризующее объект с некоторой стороны или устанавливающее отношение между несколькими объектами. Факт не требует доказательств. Факт имеет следующий вид:

<имя предиката>(O 1 ,O 2 ,…,O n).

Обратим внимание на то, что в конце факта ставится точка. <имя предиката> должно начинаться со строчной буквы и может содержать буквы, цифры, знаки подчеркивания. О i (i = 1,..,n) - аргументы предиката могут быть конкретными объектами (константами) или абстрактными объектами (переменными). Если конкретные объекты начинаются с буквы, то эта буква должна быть строчной.

Переменные начинаются с прописной буквы или символа подчеркивания. Переменная в Прологе, в отличие от алгоритмических языков программирования, обозначает объект, а не некоторую область памяти. Пролог не поддерживает механизм деструктивного присваивания, позволяющий изменять значение инициализированной переменной, как императивные языки. Переменные могут быть свободными или связанными. Свободная переменная – переменная, которая еще не получила значения. Она не равняется ни нулю, ни пробелу; у нее вообще нет никакого значения. Такие переменные еще называют неконкретизированными. Переменная, которая получила какое-то значение и оказалась связанной с определенным объектом, называется связанной. Если переменная была конкретизирована каким-то значением и ей сопоставлен некоторый объект, то эта переменная уже не может быть изменена.

Областью действия переменной в Прологе является одно предложение. В разных предложениях может использоваться одно и то же имя переменной для обозначения разных объектов. Исключением из правила определения области действия является анонимная переменная, которая обозначается символом подчеркивания. Анонимная переменная предписывает интерпретатору проигнорировать значение переменной. Если в правиле несколько анонимных переменных, то все они отличаются друг от друга, несмотря на то, что записаны с использованием одного и того же символа.

Правило – утверждение, которое истинно при выполнении некоторых условий. Правило состоит из условной части (тела) и части вывода (головы). Головой правила является предикат, истинность которого следует установить. Тело правила состоит из одного или нескольких предикатов, связанных логическими связками: конъюнкция (обозначается запятой), дизъюнкция (обозначается точкой с запятой) и отрицание (означается not или \+). Правило имеет следующий вид:

<голова правила > :–­­­­ <тело правила>.

В конце правила так же ставится точка. Можно считать, что факт – это правило, имеющее пустое тело.

С помощью правил можно описывать новые отношения.

Пример: Пусть имеется двуместное отношениеродительи одноместное отношение мужчина. Эти отношения описываются в виде фактов. Опишем новое двуместное отношениедед, используя правила.Xявляется дедомY, если существует цепочка:X– родительZ,Z– родительY, при этомXдолжен быть мужчиной.

дед(X,Y):–­­­­родитель(X,Z),родитель(Z,Y),мужчина(X).

Пример: Пусть имеется двуместное отношение родитель, описанное в виде фактов. Опишем новое двуместное отношение предок, используя правила. X является предком Y, если X – родитель Y или существует цепочка людей между Х и Y, связанных отношением родитель.

предок(X,Y):–­­­­родитель(X,Y).

предок(X,Y):–­­­­родитель(X,Z),предок(Z,Y).

Эти правила можно записать по-другому:

предок(X,Y):–­­­­родитель(X,Y);­­­

родитель(X,Z),предок(Z,Y).

В данном примере получили рекурсивное определение отношения предок.

Пример. Определим двуместное отношение дальний_родственник с помощью правила, используя имеющееся отношение предок. X является дальним родственником Y, если они связаны отношением предок, но при этом не связаны отношением родитель.

дальний_родственник (X,Y):–­­­предок(X,Y),not(родитель(X,Y));­­­

предок(Y,X),not(родитель(Y,X)).

В правой части правила для сравнения можно использовать знаки @<, @=<, @>=, @> для проверки на упорядоченность, == для проверки на равенство и \== для проверки на неравенство.

Тема 3. Язык программирования ПРОЛОГ

Лекция № 2

Язык программирования Visual Prolog

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

3. Функциональные возможности Visual Prolog

1. Основные конструкции языка Visual Prolog, разделы программы, дескриптивный, процедурный и машинный смысл программы на Прологе

Краткая историческая справка:

В начале 70 х годов группа специалистов Марсельского университета во главе с А. Колмероэ разработали специализированную систему для доказательств теорем на базе языка Фортран, которую использовали для обработки высказываний на естественном языке. В начале 80 годов в Отделении Вычислительных наук Датского Технического Университета сформировался коллектив программистов, которым в 1982 году был разработан интерпритатор языка пролог для VAX. В 1984 Лео Йенсен, Джон Гофман, Финн Гронсков разработали компилятор для IBM PC возникла фирма PDC (Prolog Development Center). Фирма Borland до 1990 Turbo Prolog затем права переданы PDC. С 1993 года под руководством Виктора Юхтенко в разработке Visual Prolog принимает участие группа PDC из СПб. Возглавляют проект в настоящее время Лео Йенсен, Томас Линдер Пулс, Виктор Юхтенко, Юрий Ильин.

1. Основные конструкции языка Visual Prolog

Имена

a. В Прологе, напомним, имена используются для обозначения символических кон­стант, доменов, предикатов и переменных. Имя состоит из буквы (или знака под­черкивания), за которой следует любая комбинация нуля или более букв, цифр или знаков подчеркивания. На имя накладываются два важных ограничения:

b. О имена символических констант должны начинаться со строчной буквы;

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

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

MyLongestVariableNameSoFar

или используя знаки подчеркивания:

pair_who_might_make_ar_happy_couple (henry_yiii, ann_boleyn)

Компилятор Visual Prolog, кроме как для первой буквы, не делает отличий между прописными и строчными буквами. Это означает, что две переменные

одинаковы.

Ключевые слова

Следующие слова являются зарезервированными и их нельзя использовать как име­на, определенные пользователем:

abstract elsedef ifndef procedure

align endclass implement protected

as enddef include predicates

and erroneous language reference

class facts multi single

clauses failure nocopy static

constants global nondeterm struct

database goal object this

Специально определенные предикаты

Следующие предикаты обрабатываются компилятором специальным образом; эти имена нельзя переопределять в программе:

chain_insertafter

2. Разделы программы

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

Опции компилятора Опции компилятора, заданные в начале модуля

constants Нуль или более символических констант

domains Нуль или более объявлений доменов

facts Нуль или более объявлений предикатов базы данных

predicates Нуль или более объявлений предикатов

goal Цель программы

clauses Нуль или более предложений

class Нуль или более объявлений открытых (и защищенных) предикатов, фактов и доменов

implement Нуль или более объявлений закрытых предикатов, фактов и доменов. Нуль или более предложений, реализующих откры­тые и закрытые предикаты (и инициализирующих факты)

abstract class Нуль или более объявлений открытых предикатов и доменов. не должны иметь реализацию

Чтобы создать программу, вы должны в ней указать цель. Программе необходимы, по меньшей мере, разделы predicates и clauses . Большинству программ нужен раздел domains для объявления списков, сложных структур и ваших собственных доменов.

При модульном программировании вы можете ставить перед ключевыми словами domains, predicates и facts ключевое слово global, указывая, что последующие объявления имеют глобальную область видимости и объявленные имена действи­тельны во всех программных модулях, которые включают объявления этих глобаль­ных разделов.

Программа может содержать несколько разделов domains, predicates, facts и clauses, а также несколько объявлений и реализаций классов,

Секция объявления объектов предметной области (domains sec­tion), которые будут использоваться в логической программе, например:

title, author = symbol pages = unsigned

секция объявления предикатов (predicates section), которые будут использоваться в логической программе, например:

book(title, pages) written_by(author, title) long_novel(title)

Секция логических предложений (clauses section), в которой указываются факты и правила, используемые системой Prolog для поиска решений поставленных перед ней задач, например:

секция цели (goal section), в которой формулируется задача для системы Prolog, например:

3. Дескриптивный, процедурный и машинный смысл программы на Прологе.

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

Программы на языке Пролог состоят из двух типов фраз: фактов и правил, также называемых предложениями.

· Факты - это отношения или свойства, о которых известно, что они имеют значение "истина".

· Правила - это связанные отношения; они позволяют Прологу логически вы­водить одну порцию информации из другой. Правило принимает значение "истина", если доказано, что заданный набор условий является истинным.

2. В Прологе все правила имеют 2 части: заголовок и тело, разделенные специаль­ным знаком: -.

· Заголовок - это факт, который был бы истинным, если бы были истинными несколько условий. Это называется выводом или зависимым отношением.

· Тело - это ряд условий, которые должны быть истинными, чтобы Пролог мог доказать, что заголовок правила истинен.

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

Пролог всегда ищет решение, начиная с первого факта и/или правила, и просматри­вает весь список фактов и/или правил до конца.

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

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

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

Язык Пролог не предназначен для программирования задач с большим количеством арифметических операций. Для этого используются процедурные языки программирования. Однако в любую Пролог-систему включаются все обычные арифметические операторы:

Сложение - вычитание * умножение / деление mod остаток от деления целых чисел div целочисленное деление

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

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

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

2. структуры данных в программах на Прологе.

1. Представление знаний о предметной области в виде фактов и правил базы знаний Пролога.

Деревья относятся к рекурсивные структурам данных

Тип данных является рекурсивным, если он допускает структуры, содержащие такие же структуры, как и они сами.

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

Visual Prolog позволяет определить действительно рекурсивные типы, в которых ука­затели создаются и обрабатываются автоматически. Например, можно определить дерево следующим образом:

treetype = tree(string, treetype, treetype)

Эта декларация говорит о том, что дерево записывается как функтор tree, аргумен­тами которого являются строка и два других дерева.

Внутренняя база фактов (данных)

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

Для добавления новых фактов в базу данных в Visual Prolog используются предикаты assert, asserta, assertz, а предикаты retract и retractall служат для удаления существующих фактов. Вы можете изменить содержание вашей базы фактов, снача­ла удалив факт, а потом вставив новую версию этого факта (или совершенно другой факт). Предикаты consult/1 и consult/2 считывают факты из файла и добавляют их к внутренней базе данных, a save/1 и save/2 сохраняют содержимое внутренней базы фактов в файле.

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

Ключевое слово facts (это синоним устаревшего слова database) определяет начало объявления раздела facts

Внешние базы данных в Visual Prolog

Система внутренних баз данных Visual Prolog, использующая предикаты asserta, assertz, retract и retractall, является простой и удобной. Однако она уступает в скорости работы с большими базами данных, отчасти из-за этих соображений была создана система внешних баз данных, с помощью которой можно, например, создать:

Систему управления запасами с большим количеством записей;

Экспертную систему со многими отношениями и небольшим количеством запи­сей сложной структуры;

Учетную систему для запоминания больших текстов в базе данных;

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

2. структуры данных в программах на Прологе

Списки и рекурсия

Обработка списков, т. е. объектов, которые содержат произвольное число элемен­тов - мощное средство Пролога. В этой главе объясняется, что такое списки и как их объявлять. Затем приводится несколько примеров, в которых показано, как можно использовать обработку списков в задачах. Далее определяются два извест­ных предиката Пролога - member (член) и append (объединение) при рассмотрении процедурных и рекурсивных аспектов обработки списков.

После этого определяется стандартный предикат Visual Prolog - findall, который дает возможность находить и собирать все решения для одной цели.

Что такое список?

В Прологе список - это объект, который содержит конечное число других объектов. Списки можно грубо сравнить с массивами в других языках, но, в отличие от масси­вов, для списков нет необходимости заранее объявлять их размер.

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

Каждая составляющая списка называется элементом. Чтобы оформить списочную структуру данных, надо отделить элементы списка запятыми и заключить их в квад­ратные скобки. Вот несколько примеров:

["valerie ann", "Jennifer caitlin", "benjamin thomas"]

Объявление списков

Чтобы объявить домен для списка целых, надо использовать декларацию домена, такую как:

integerlist = integer*

Символ (*) означает "список чего-либо"; таким образом, integer* означает "список целых".

Обратите внимание, что у слова "список" нет специального значения в Visual Prolog. С тем же успехом можно назвать список "Занзибаром". Именно обозначение * (а не название), говорит компилятору, что это список.

Элементы списка могут быть любыми, включая другие списки. Однако все его эле­менты должны принадлежать одному домену. Декларация домена для элементов должна быть следующего вида:

elementlist = elements*

Здесь elements имеют единый тип (например: integer, real или symbol) или явля­ются набором отличных друг от друга элементов, отмеченных разными функторами. В Visual Prolog нельзя смешивать стандартные типы в списке. Например, следующая декларация неправильно определяет список, составленный из элементов, являющих­ся целыми и действительными числами или идентификаторами:

Головы и хвосты

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

голова [а, b, с] есть a

хвост [а, b, с] есть

Что происходит, когда вы доходите до одноэлементного списка? Ответ таков

голова[с] есть с

хвост [с] есть

Работа со списками

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

[а, b, с] эквивалентно [а| ] и, продолжая процесс,

[а | ] эквивалентно [а | ]], что эквивалентно [а| ] ]

Можно использовать оба вида разделителей в одном и том же списке при условии, что вертикальная черта есть последний разделитель. При желании можно набрать [а, b, с, d] как [а, b [с, d] ].

Использование списков

Список является рекурсивной составной структурой данных, поэтому нужны алго­ритмы для его обработки. Главный способ обработки списка - это просмотр и об­работка каждого его элемента, пока не будет достигнут конец.

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

Подсчет элементов списка

Проверка нахождения в списке

3. Функциональные возможности Visual Prolog

1. Модульное программирование

2. объектный механизм

3. Средства создания графического интерфейса

1. Модульное программирование

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

Глобальные объявления

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

Начиная с версии 5.2, Visual Prolog обеспечивает управление глобальными объявлениями. А именно:

Главный модуль проекта (с разделом цели goal) должен содержать объявления всех глобальных доменов (и разделов глобальных фактов), объявленных во всех подмодулях проекта;

Любой другой проектный модуль содержит объявления только тех глобальных доменов, которые используются в этом модуле;

Глобальные объявления могут помещаться после локальных;

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

Глобальные домены

Вы делаете домен глобальным, записав его в разделе global domains. Во всем ос­тальном глобальные домены аналогичны локальным.

Visual Prolog версии 5.2 предоставляет усовершенствованное управление глобальны­ми доменами. Теперь не требуется, чтобы все модули содержали одинаковые объявления всех глобальных доменов в определенном порядке (в PDC Prolog и в версиях Visual Prolog до 5.2 для проверки этой идентичности использовалась специальная утилита chkdoms. exe). Теперь же вам нужно следовать двум правилам:

Только главный модуль проекта (с разделом цели) должен содержать объявления всех глобальных доменов (и разделов глобальных фактов), объявленных во всех подмодулях проекта;

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

Это дает следующие принципиальные преимущества:

Возможность создания и использования заранее скомпилированных библиотек (использующих глобальные домены);

Уменьшение времени компиляции: если изменяется какое-либо глобальное объ­явление, то должны быть перекомпилированы только модули, включающие это объявление;

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

Среда визуальной разработки Visual Prolog предоставляет гибкий автоматический механизм для управления включением объявлений глобальных доменов в модули проекта. Ядро этого механизма - диалоговое окно File Inclusion for Module, которое активизируется при создании нового модуля. Для небольших проектов вы можете использовать упрощенную стратегию обеспечения включения всех глобальных доме­нов в каждый проектный модуль. Для этого вам нужно:

Глобальные секции фактов

Раздел facts будет глобальным, если перед ключевым словом facts (можно исполь­зовать и устаревшее ключевое слово database) вставлено ключевое слово global.

Замечание

Размещать обязательно требующиеся инициализирующие предложения для single-фактов из глобальных секций фактов (баз данных) можно только после раз­дела goal в главном модуле проекта.

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

проекты

Если вы используете VDE, то эксперт приложений автоматически управляет созда­нием новых проектов и добавлением/удалением проектных модулей. Построитель программ (Make facility) VDE автоматически совершает операции компиляции и компоновки, необходимые для создания целевого модуля из исходных модулей проекта.

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

Как Visual Prolog-проект использует таблицу символов.

Visual Prolog-программы должны иметь цель (содержать Goal-секцию).

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

Классы и объекты

Visual Prolog включает в себя объектный механизм, объединяющий парадигмы логи­ческого и объектно-ориентированного программирования (ООП).

Для того чтобы система могла рассматриваться как объектно-ориентированная, не­обходимо, чтобы она удовлетворяла четырем критериям. Это:

 инкапсуляция;

 наследование;

 индивидуальность.

Инкапсуляция

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

ООП также известно как программирование, управляемое данными. Фактически, вы позволяете объектам делать работу самим. Они содержат методы, которые вызыва­ются при создании объектов, при их удалении и, вообще, при любых обращениях к объектам. Методы могут вызывать и методы других объектов.

Объекты и классы

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

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

Класс - это определяемый пользователем тип объекта; класс может создавать объек­ты этого типа. Экземпляр - это фактическое состояние объекта. Вы можете опреде­лить столько экземпляров (объектов) класса, сколько вам необходимо.

Наследование

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

Индивидуальность

Каждый объект уникален. Объекты изменяют свое состояние, и, т. к. состояния объ­ектов можно наблюдать только посредством их предикатов-членов (т. е. предикатов, объявленных внутри определения класса), объект идентичен лишь самому себе. Таким образом, даже если состояния двух объектов одинаковы, объекты не являются идентичными, ибо мы можем изменять состояние одного объекта, не изменяя при этом состояния другого, и в этом случае объекты становятся не идентичными.

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

Средства создания графического интерфейса

Визуальный интерфейс программирования VPI (Visual Programming Interface) - вы­сокоуровневый программный интерфейс, разработанный для облегчения создания программ на Visual Prolog, способный поддерживать сложные интерфейсы, исполь­зующие графические возможности современных операционных систем. Окна, меню, диалоговые окна, элементы управления, перья, кисти, курсоры, рисунки и т. д. - все эти ресурсы и инструментальные средства входят в состав Visual Prolog как про­стые структуры,

VPI делает возможным создание переносимого исходного кода, который может быть откомпилирован, чтобы работать в 16-разрядной конфигурации под MS Windows 3.1*, 32-разрядной - под Windows 95/98/ME, Windows NT/2000, под администратором представлений OS/2.

Управляемые событиями приложения

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

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

Вообще, во время работы могут возникать самые разные ситуации, например, поль­зователь может получить срочный телефонный звонок. На него можно ответить, выбрав системную кнопку окна Task или меню файла и закрыв приложение. Опера­ционная система сообщает приложению о событии, посылая сообщение обработчи­ку событий окна Task . В большинстве многозадачных системах операционная сис­тема может приостановить ваше приложение в любое время. Приложение никогда не будет знать, запрашивал ли пользователь закрытие или это операционная система обнаружила сбой питания. Или если приложение хочет закрыть какое-то окно, то оно посылает этому окну запрос на закрытие CloseRequest точно так же, как поль­зователь, когда он выбирает команду Close в меню окна Task .

Конечно, вы можете уменьшить количество возможных событий и сообщений, бло­кируя пункты меню и кнопки управления. Часть обработки сообщений GUI-приложений может быть очень сложной, но это цена за большую гибкость. Про­лог и VPI существенно упрощают работу программиста по связыванию приложения с GUI операционной системы. Эксперт кода и браузер (The Code Expert and the Browser) облегчают поиск и обработку кода, который соответствует определенному событию, отвечая на определенное сообщение, и вы редко будете иметь дело с запу­танными деталями основной логики передачи сообщений.

Окна

Окно представляет собой прямоугольное пространство на экране. Окно используется приложением для вывода и ввода данных . Все пространство экрана рассматривается как специальное "экранное окно". Каждое окно разделяет экран с другими окнами, включая окна иных приложений, а иногда и операционной системы. Эти окна могут перекрывать друг друга.

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

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

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

Типы окон

Окна классифицируются по типам. Типы окон должны принадлежать домену windowtype и иметь префиксы w_ (для окон), wc_ (для элементов управления) или wd_ (для диалоговых окон). Тип окна может быть получен по дескриптору окна вызовом предиката win_GetType. Возможные типы окон перечислены.

Обычное окно документа

Дочернее окно

Окно экрана Screen

Модальное диалоговое окно

Немодальное диалоговое окно

Командная кнопка

Переключатель

Горизонтальная полоса прокрутки

Вертикальная полоса прокрутки

Поле редактирования

Статический текст

Раскрывающийся список

Поле редактирования с раскрывающимся списком

Групповой блок

Пиктограмма

Специальные (определяемые пользователем) элементы управления

Внутреннее окно, используемое во время печати

Внутреннее окно, используемое во время создания изображений

Внутреннее окно, используемое во время создания метафайлов/

Язык программирования

Prolog (от “PROgramming in LOGic”) - декларативный язык программирования общего назначения.

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

Стандарт языка дан в ISO/IEC 13211-1 (1995 год).

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

Prolog был создан под влиянием более раннего языка Planner и позаимствовал из него следующие идеи:

  • обратный логический вывод (вызов процедур по шаблону, исходя из целей);
  • построение структура управляющей логики в виде вычислений с откатами;
  • принцип “отрицание как неудача”;
  • использование разных имен для разных сущностей и т.д.

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

Prolog использует один тип данных, терм, который бывает нескольких типов:

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

Программы, написанные на чистом Prolog, описывают отношения между обрабатываемыми сущностями при помощи клауз Хорна. Клауза — это формула вида Голова:- Тело. , которая читается как “чтобы доказать/решить Голову, следует доказать/решить Тело”. Тело клаузы состоит из нескольких предикатов (целей клаузы), скомбинированных с помощью конъюнкции и дизъюнкции. Клаузы с пустым телом называются фактами и эквивалентны клаузам вида Голова:- true. (true — не атом, как в других языках, а встроенный предикат).

Другой важной частью Prolog являются предикаты. Унарные предикаты выражают свойства их аргументов, тогда как предикаты с несколькими аргументами выражают отношения между ними. Ряд встроенных предикатов языка выполняют ту же роль, что и функции в других языках, например, ….

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

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

Целью выполнения программы на Prolog является оценивание одного целевого предиката. Имея этот предикат и набор правил и фактов, заданных в программе, Prolog пытается найти привязки (значения) переменных, при которых целевой предикат принимает значение истинности.

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

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

Пролог (Prolog) - язык логического программирования, основанный на логике дизъюнктов Хорна, представляющей собой подмножество логики предикатов первого порядка. Начало истории языка относится к 70-м годам XX века. Будучи декларативным языком программирования, Пролог воспринимает в качестве программы некоторое описание задачи, и сам производит поиск решения, пользуясь механизмом бэктрекинга и унификацией.

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

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

Пролог реализован практически для всех известных операционных систем и платформ. В число операционных систем входят OS для мэйнфреймов, всё семейство Unix, Windows, OS для мобильных платформ.

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

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

Основные вехи развития языка Prolog

Prolog стал воплощением идеи использования логики в качестве языка программирования, которая зародилась в начале 1970-х годов, и само его название является сокращением от слов “programming in logic” (программирование в терминах логики). Первыми исследователями, которые занялись разработкой этой идеи, были Роберт Ковальски (Robert Kowalski) из Эдинбурга (теоретические основы), Маартен ван Эмден (Maarten van Emden) из Эдинбурга (экспериментальная демонстрационная система) и Ален Колмероэ (Alain Colmerauer) из Марселя (реализация). Популяризации языка Prolog во многом способствовала эффективная реализация этого языка в середине 1970-х годов Дэвидом Д. Г. Уорреном (David D.H. Warren) из Эдинбурга. К числу новейших достижений в этой области относятся средства программирования на основе логики ограничений (Constraint Logic Programming - CLP), которые обычно реализуются в составе системы Prolog. Средства CLP показали себя на практике как исключительно гибкий инструмент для решения задач составления расписаний и планирования материально-технического снабжения. А в 1996 году был опубликован официальный стандарт ISO языка Prolog.

Наиболее заметные тенденции в истории развития языка Prolog

В развитии языка Prolog наблюдаются очень интересные тенденции. Этот язык быстро приобрел популярность в Европе как инструмент практического программирования. В Японии вокруг языка Prolog были сосредоточены все разработки компьютеров пятого поколения. С другой стороны, в США этот язык в целом был принят с небольшим опозданием в связи с некоторыми историческими причинами. Одна из них состояла в том, что Соединенные Штаты вначале познакомились с языком Microplanner, который также был близок к идее логического программирования, но неэффективно реализован. Определенная доля низкой популярности Prolog в этой стране объясняется также реакцией на существовавшую вначале “ортодоксальную школу” логического программирования, представители которой настаивали на использовании чистой логики и требовали, чтобы логический подход не был “запятнан” практическими средствами, не относящимися к логике. В прошлом это привело к широкому распространению неверных взглядов на язык Prolog. Например, некоторые считали, что на этом языке можно программировать только рассуждения с выводом от целей к фактам. Но истина заключается в том, что Prolog - универсальный язык программирования и на нем может быть реализован любой алгоритм. Далекая от реальности позиция “ортодоксальной школы” была преодолена практиками языка Prolog, которые приняли более прагматический подход, воспользовавшись плодотворным объединением нового, декларативного подхода с традиционным, процедурным.

Элементы синтаксиса:

Комментарий до конца строки %
Регистрозависимость да
Регулярное выражение идентификатора переменной [_A-Z][_a-zA-Z0-9]*
Регулярное выражение идентификатора функции [_a-z][_a-zA-Z0-9]*
Присваивание значения переменной is
Объявление переменной = или:-
Группировка выражений (...)
Тождественное равенство ==
Тождественное неравенство \==
Сравнение @< @=< @> @>=
Вызов функции f(a,b,...)
Вызов функции без параметров f
Последовательность ,
Если - то - иначе (A -> B ; C)
Цикл с постусловием repeat, ..., condition

Примеры:

Hello, World!:

Пример для версий Visual Prolog 7.2

Visual Prolog создает проекты автоматически. Для запуска примера следует создать новый проект, выбрав “Console” в качестве UI Strategy, перейти к редактированию файла main.pro и заменить его содержимое приведенным кодом.

implement main open core constants className = "main" . classVersion = "" . clauses classInfo (className , classVersion ). clauses run ():- console : : init (), stdio : : write ("Hello, World!" ), programControl:: sleep (1000 ), succeed (). end implement main goal mainExe:: run (main : :run ).

Факториал:

Пример для версий Visual Prolog 7.2

В main.cl добавлена одна строка factorial: (integer N, integer F) procedure (i,o). , которая определяет бинарный предикат factorial с известным первым и неизвестным вторым аргументами. Ключевое слово procedure описывает поведение предиката, указывая, что его вычисление всегда будет успешным и будет найдено ровно одно решение, так что откаты не понадобятся.

В main.pro находится собственно определение нового предиката. Для каждого его вызова есть два возможных соответствия — с нулевым или произвольным первым аргументом. Visual Prolog перебирает формулы в порядке их появления в коде, так что если первый аргумент равен нулю, проверка начинается с первой формулы factorial(0,F) . Первое правило формулы — !, так называемое отсечение, использование которого предотвращает откат ко второй формуле и таким образом обеспечивает наличие ровно одного решения предиката. После этого переменная F, содержащая решение предиката, устанавливается в 1 и выводится на печать. Вторая формула factorial(N,F) рекурсивно вычисляет F1 как факториал N-1, устанавливает решение предиката равным N*F1 и выводит его на печать. Наконец, stdio::nl печатает новую строку.

При выполнении основной программы предикат factorial выполняется ровно один раз, для N=12. С каждым вызовом рекурсии N уменьшается на единицу, пока не становится равным нулю. После этого значения факториалов возвращаются и выводятся на печать в порядке возрастания. Программа обрабатывает только факториалы до 12!, т.к. попытка вычисления 13! вызывает ошибку переполнения целочисленного типа.

% main.cl class main open core predicates classInfo : core : :classInfo . factorial : (integer N , integer F ) procedure (i , o ). predicates run : core : :runnable . end class main % main.pro implement main open core constants className = "main" . classVersion = "" . clauses classInfo (className , classVersion ). factorial (0 , F ) :- !, F = 1 , stdio : : write ("0! = 1" ), stdio : :nl . factorial (N , F ) :- factorial (N - 1 , F1 ), F = N * F1 , stdio : : write (N , "! = " , F ), stdio : :nl . clauses run ():- console : : init (), factorial (12 , F ), programControl:: sleep (1000 ), succeed (). end implement main goal mainExe:: run (main : :run ).

Числа Фибоначчи:

Пример для версий Visual Prolog 7.2

В этом примере определяются два новых предиката — бинарный fibonacci(N,F) для вычисления N-ого числа Фибоначчи и loop(N) для его вывода на печать. Единожды вычисленные числа не сохраняются для позднейшего использования, поэтому эта реализация неэффективна.

Следует отметить отличие реализаций предикатов от примера для факториала: формулы, описывающие начальные условия, задаются для произвольного значения переменной, но вычисляются до конца только в том случае, если первое правило (N<3 или N=1 , соответственно) оценивается как истинное. Кроме того, каждый предикат записан как одна формула, использующая конъюнкцию и дизъюнкцию, а не как набор отдельных формул, использующих только конъюнкцию.

% main.cl class main open core predicates classInfo : core : :classInfo . fibonacci : (integer N , integer F ) procedure (i , o ). loop : (integer N ) procedure (i ). predicates run : core : :runnable . end class main % main.pro implement main open core constants className = "main" . classVersion = "" . clauses classInfo (className , classVersion ). fibonacci (N , F ) :- N < 3 , !, F = 1 ; fibonacci (N - 1 , F1 ), fibonacci (N - 2 , F2 ), F = F1 + F2 . loop (N ) :- ( N = 1 , !, fibonacci (1 , F ); loop (N - 1 ), fibonacci (N , F ) ), stdio : : write (F , ", " ). clauses run ():- console : : init (), loop (16 ), stdio : : write ("..." ), programControl:: sleep (1000 ), succeed (). end implement main goal mainExe:: run (main : :run ).

Hello, World!:

Пример для версий B-Prolog 7.4-3 , ECLiPSe CLP 6.0 #188 , Poplog 15.5 (Prolog) , gprolog 1.3.0 , swipl 5.6.x

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

Hello, World!
yes

Первая строка является собственно выводом предиката write , вторая — результат оценивания запроса.

Следует отметить, что замена одинарных кавычек на двойные выводит строку как массив ASCII-кодов отдельных символов:

| ?- write("Hello, World!").

write ("Hello, World!" ), nl .

Числа Фибоначчи:

Пример для версий Poplog 15.5 (Prolog)

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

После этого предикат fib(N,F) определяется рекурсивно, но каждый вызов fib “обернут” в предикат memo , поэтому для каждого значения N fib(N,F) оценивается только один раз. При таком подходе печать вычисленных чисел может производиться сразу после их вычисления, без дополнительного цикла.

% fibonacci.pl :- dynamic (stored / 1 ). memo (Goal ) :- stored (Goal ) -> true ; Goal , assertz (stored (Goal )). fib (1 , 1 ) :- !, write ("1, " ). fib (2 , 1 ) :- !, write ("1, " ). fib (N , F ) :- N1 is N - 1 , memo (fib (N1 , F1 )), N2 is N - 2 , memo (fib (N2 , F2 )), F is F1 + F2 , write (F ), write (", " ). % interactive [ - fibonacci ]. fib (16 , X ), write ("..." ), nl .

Факториал:

Пример для версий Poplog 15.5 (Prolog)

Этот пример состоит из двух частей — первую часть кода следует сохранить в файле fact.pl , расположенном в рабочем каталоге Poplog, а вторую — ввести вручную в интерактивном режиме.

[-fact]. загружает базу фактов и правил из этого файла в текущую сессию Poplog (и выводит сообщение fact reconsulted , чтобы обозначить успешность загрузки). Запрос fact(16,X). пытается найти значение X, при котором этот предикат будет оценен как истинный. Вывод, требующийся в примере, будет побочным эффектом оценивания запроса, а основным результатом будет X = 20922789888000 ? . Это означает, что если вы недовольны такой привязкой переменных, вы можете отказаться от нее (введя;), и будет продолжен поиск лучшей привязки.

% fact.pl fact (X , F ) :- ( X = 0 , F = 1 ; Y is X - 1 , fact (Y , Z ), F is X * Z ), write (X ), write ("! = " ), write (F ), nl . % interactive [ - fact ]. fact (16 , X ).

Квадратное уравнение:

Пример для версий Visual Prolog 7.2

Для запуска создайте новый проект с UI Strategy “Console” и замените содержимое файлов main.cl и main.pro приведенным кодом.

В main.cl добавлена одна строка q: () procedure(). . Ключевое слово procedure описывает поведение предиката, указывая, что его вычисление всегда будет успешным и будет найдено ровно одно решение, так что откаты не понадобятся.

В main.pro находится собственно определение нового предиката. Предикат q не принимает аргументов, поскольку читает необходимые данные из stdio . Условное оценивание (конструкция if-then-else) работает точно так же, как в других языках. Единственным отличием является знак отсечения! перед then . Это означает, что как только условие if выполняется, откат уже не потребуется.

Хитрость этого примера в том, что невозможно сразу вычислить дискриминант, как в других языках. Тип данных по умолчанию для переменной D в присвоении D = B*B-4*A*C - uReal , который может хранить только неотрицательные числа.

% main.cl class main open core predicates classInfo : core : :classInfo . q : () procedure (). predicates run : core : :runnable . end class main % main.pro implement main open core constants className = "main" . classVersion = "" . clauses classInfo (className , classVersion ). q () :- stdio : : write ("A = " ), A = stdio : : read (), if (A = 0 ), ! then stdio : : write ("Not a quadratic equation." ), stdio : :nl else stdio : : write ("B = " ), B = stdio : : read (), stdio : : write ("C = " ), C = stdio : : read (), if (B * B = 4 * A * C ), ! then stdio : : writef ("x = %f" , - B / 2 . 0 / A ) elseif (B * B > 4 * A * C ), ! then D = B * B - 4 * A * C , stdio : : writef ("x1 = %f\n" , (- B + math : : sqrt (D )) / 2 . 0 / A ), stdio : : writef ("x2 = %f" , (- B - math : : sqrt (D )) / 2 . 0 / A ) else D = - B * B + 4 * A * C , stdio : : writef ("x1 = (%f, %f)\n" , - B / 2 . 0 / A , math : : sqrt (D ) / 2 . 0 / A ), stdio : : writef ("x2 = (%f, %f)" , - B / 2 . 0 / A , - math : : sqrt (D ) / 2 . 0 / A ) end if end if . clauses run ():- console : : init (), q (), succeed (). end implement main goal mainExe:: run (main : :run ).

Факториал:

Пример для версий B-Prolog 7.4-3 , gprolog 1.3.0 , swipl 5.6.x

Как в GNU Prolog , так и в B-Prolog 12! не помещается в целочисленный тип данных, поэтому все значения после 11! неправильны. В SWI-Prolog переполнения не возникает.

Результат для GNU Prolog: compiling /home/nickolas/Desktop/progopedia/prolog/fact.pl for byte code…
/home/nickolas/Desktop/progopedia/prolog/fact.pl compiled, 3 lines read - 1372 bytes written, 5 ms

Результат для B-Prolog: consulting::fact.pl

`| ?- fact(16,X).

0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = -57869312
13! = -215430144
14! = 205203456
15! = -143173632
16! = -143294464

X = -143294464 ?`

% fact.pl fact (X , F ) :- ( X = 0 , F = 1 ; Y is X - 1 , fact (Y , Z ), F is X * Z ), write (X ), write ("! = " ), write (F ), nl . % interactive [ fact ]. fact (16 , X ).

Числа Фибоначчи:

Пример для версий B-Prolog 7.4-3 , gprolog 1.3.0 , swipl 5.6.x

Пример почти идентичен примеру для , за исключением синтаксиса подключения файла.

% fibonacci.pl :- dynamic (stored / 1 ). memo (Goal ) :- stored (Goal ) -> true ; Goal , assertz (stored (Goal )). fib (1 , 1 ) :- !, write ("1, " ). fib (2 , 1 ) :- !, write ("1, " ). fib (N , F ) :- N1 is N - 1 , memo (fib (N1 , F1 )), N2 is N - 2 , memo (fib (N2 , F2 )), F is F1 + F2 , write (F ), write (", " ). % interactive [ fibonacci ]. fib (16 , X ), write ("..." ), nl .

Квадратное уравнение:

Пример для версий gprolog 1.3.0

read_integer — не стандартный предикат, а расширение GNU Prolog, поэтому этот пример не будет работать в других реализациях.

q :- write ("A = " ), read_integer (A ), ( A = 0 , write ("Not a quadratic equation" ); write ("B = " ), read_integer (B ), write ("C = " ), read_integer (C ), D is B * B - 4 * A * C , ( D = 0 , write ("x = " ), X is - B / 2 / A , write (X ); D > 0 , write ("x1 = " ), X1 is (- B + sqrt (D )) / 2 / A , write (X1 ), nl , write ("x2 = " ), X2 is (- B - sqrt (D )) / 2 / A , write (X2 ); R is - B / 2 / A , I is abs (sqrt (- D ) / 2 / A ), write ("x1 = (" ), write (R ), write (", " ), write (I ), write (")" ), nl , write ("x1 = (" ), write (R ), write (", -" ), write (I ), write (")" ) ) ).

Квадратное уравнение:

is B * B - 4 * A * C , ( D = 0 , write ("x = " ), X is - B / 2 / A , write (X ); D > 0 , write ("x1 = " ), X1 is (- B + sqrt (D )) / 2 / A , write (X1 ), nl , write ("x2 = " ), X2 is (- B - sqrt (D )) / 2 / A , write (X2 ); R is - B / 2 / A , I is abs (sqrt (- D ) / 2 / A ), write ("x1 = (" ), write (R ), write (", " ), write ((N , F ) :- N > 0 , N1 is N - 1 , factorial (N1 , F1 ), F is N * F1 . main :- ( for (N , 0 , 16 ) do factorial (N , F ), write (N ), write ("! = " ), write (F ), nl ). (N1 , F1 ), fibonacci (N2 , F2 ), F is F1 + F2 . main :- ( for (N , 1 , 16 ) do fibonacci (N , F ), write (F ), write (", " ) ), writeln ("..." ).

Числа Фибоначчи:

Пример для версий ECLiPSe CLP 6.0 #188

Числа Фибоначчи вычисляются рекурсивно, при этом используется мемоизация, реализованная при помощи ECLiPSe-специфичного механизма store .

Для организации цикла в предикате main используется специфичная для ECLiPSe итеративная управляющая структура (мета-предикат) for .

:- local store (fibonacci ). fibonacci (1 , 1 ) :- !. fibonacci (2 , 1 ) :- !. fibonacci (N , F ) :- N > 2 , ( % используем сохраненный результат store_get (fibonacci , N , F ), ! ; N1 is N - 1 , N2 is N - 2 , fibonacci (N1 , F1 ), fibonacci (N2 , F2 ), F is F1 + F2 , store_set (fibonacci , N , F ) % сохраняем полученный результат ). main :- ( for (N , 1 , 16 ) do fibonacci (N , F ), write (F ), write (", " ) ), writeln ("..." ).



Лекция посвящена решению задач при помощи графа пространства состояний. Пространство состояний описывается в виде множества состояний – вершин графа, множества переходов от состояния к состоянию – дуг графа, множества начальных состояний и множества конечных состояний. Решение задачи представляется в виде пути на графе пространства состояний, соединяющего начальное состояние с конечным. Если пространство состояний задачи невелико, то будут находиться все оптимальные решения с помощью поиска в глубину. В задачах с большим пространством состояний будет вычисляться только одно оптимальное решение посредством поиска в ширину. К задачам применяются универсальные решатели. Состояния в разных задачах могут принадлежать различным доменам. Для запоминания наилучших среди найденных решений используется "изменяемая переменная" varM. Компилятор сам находит нужные типы. Версия Visual Prolog 7.5, еще не опубликована. Ее публикация планируется в 2014 году, но точная дата пока не известна. Сейчас всем доступна версия Visual Prolog 7.4.