Главная страница
Навигация по странице:

  • Основы логического программирования Введение

  • Предложения: факты и правила

  • Основные секции программы

  • Основные стандартные домены

  • Поиск с возвратом

  • Основы логического и функционального программирования.prn. Основы логического и функционального программирования


    Скачать 410 Kb.
    НазваниеОсновы логического и функционального программирования
    Дата05.01.2022
    Размер410 Kb.
    Формат файлаdoc
    Имя файлаОсновы логического и функционального программирования.prn.doc
    ТипУчебное пособие
    #324347
    страница1 из 5
      1   2   3   4   5

    Новосибирский государственный технический университет

    Кафедра вычислительной техники

    Ю. В. Новицкая

    Основы логического и функционального программирования


    Учебное пособие

    Новосибирск

    2006 г.

    Основы логического программирования 3

    1.1.Введение 3

    1.2.Предложения: факты и правила 4

    1.3.Запросы 5

    1.4.Предикаты 5

    1.5.Переменные 6

    1.6.Основные секции программы 8

    1.7.Основные стандартные домены 8

    1.8.Поиск с возвратом 9

    1.9.Управление поиском с возвратом: предикаты ! и fail 13

    1.10.Рекурсия 20

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

    1.12.Списки 33

    1.13.Деревья 39

    1.14.Строки 44

    1.15.Динамические базы данных 45

    Основы функционального программирования 46

    2.1.Введение 46

    2.2.Символьные выражения 46

    2.3.Списки 46

    2.4.Функции 47

    2.5.Базовые функции 49

    2.6.Управляющие структуры (предложения) 51

    2.7.Простая рекурсия 53

    2.8.Другие виды рекурсии 56

    Литература 59

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

    1. Введение

    Рассмотрим пример программы на Prolog’е:

    Например, необходимо выявить все объекты, имеющие свойство быть птицей.

    Предположим, имеются следующие исходные данные:

    • журавль – это птица;

    • у синицы есть крылья;

    • синица умеет летать;

    • у пингвина есть крылья;

    • пингвин умеет плавать;

    • некто является птицей при условии, что у него есть крылья и он умеет летать.

    Первые пять предложений будем считать не подлежащими сомнению и назовем фактами, шестое предложение – правилом вывода, так как это предложение формулирует правило, по которому можно сделать вывод о том, является ли некто птицей или нет. Некто будет птицей при условии, что он умеет летать и у него есть крылья. Условное обозначение :- читается: «при условии» или «если». Так как условия «умеет летать» и «есть крылья» перечислены через запятую, они должны быть выполнены оба. Запятая означает операцию «логическое И». Все предложения должны заканчиваться точкой.

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

    птица (журавль).

    есть_крылья (синица).

    умеет_летать (синица).

    есть_крылья (пингвин).

    умеет_плавать (пингвин).

    птица (Объект):- есть_крылья (Объект), умеет_летать (Объект).

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

    Теперь к программе можно обращаться с вопросами (запросами):

    1. Кто является птицей? (Ответы: журавль, синица)

    2. Кто умеет летать? (Ответ: синица)

    3. У кого есть крылья? (Ответы: синица, пингвин)

    4. и т.д.

    Рассмотрим, каким образом работает программа. Предположим, нужно найти ответ на первый вопрос – кто является птицей? Вопрос будет иметь такой вид:

    Goal: птица (Кто).

    Кто – это имя переменной, которая в начале работы программы не имеет никакого значения.

    Каким образом будет находиться первое решение? Будут последовательно просматриваться все строки программы и первая же строка (первый факт) дает первое решение – журавль.

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

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

    Теперь, для того, чтобы правило вывода дало второе решение, необходимо, чтобы были выполнены все условия, записанные в его правой части. Первое условие выполнится, если будет найден объект, у которого есть крылья. Другими словами, в тексте программы нужно найти факт, говорящий об этом. При просмотре программы с самого начала такой факт обнаруживается – у синицы есть крылья. Переменная Объект немедленно принимает значение «синица». Проверка выполнения второго условия начинается с учетом того, что переменная Объект уже имеет значение «синица», то есть второе условие в правиле вывода имеет вид – умеет_летать (синица). Но второе условие еще нельзя считать выполненным. Недостаточно того, что переменная Объект приняла значение «синица», нужно найти факт, подтверждающий, что синица действительно умеет летать. А для этого вновь просмотреть предложения программы с самого начала. Факт находится. Найдено второе решение «синица». Больше решений обнаружить не удастся, так как больше нет фактов, описывающий птиц, у которых есть крылья и которые умеют летать.

    Итак, полученные решения:

    1. журавль (в соответствии с фактом)

    2. синица (по правилу вывода)

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

    есть_крылья (самолет).

    умеет_летать (самолет).

    будет найдено третье решение – самолет.

    1. Предложения: факты и правила

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

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

    Примеры фактов:

    %объект кот имеет свойство – черный цвет или, проще, кот – черный

    black (cat).

    %города Новосибирск и Омск связаны железной дорогой

    railway (novosibirsk, omsk).

    Второй тип предложений – правила вывода. Правило вывода состоит из двух частей, разделенных условным обозначением :- , которое читается как «если», или «при условии, что». Левая часть правила вывода называется заголовком или головной целью. Правая часть правила вывода называется хвостом или хвостовой частью. Хвостовая часть может состоять из нескольких условий (хвостовых целей), перечисленных через запятую или точку с запятой. Запятая означает операцию «логическое И», точка с запятой – операцию «логическое ИЛИ». Использовать скобки в хвостовой части правила вывода нельзя.

    Головная цель правила вывода считается доказанной, если доказаны все хвостовые цели в правой части правила вывода.

    Пример правил вывода:

    %Некоторый объект, обозначенный переменной Bird, является пингвином,

    %если объект умеет плавать и у него есть крылья

    penguin (Bird):- swim (Bird), wings(Bird).

    %Некоторый объект, обозначенный переменной Bird, является страусом,

    %если объект не умеет летать, у него длинные ноги и есть крылья

    ostrich (Bird):- not_fly (Bird), long_legs (Bird), wings(Bird).

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

    1. Запросы

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

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

    1. Предикаты

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

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

    Например, факт black (cat). записан с использованием предиката black, имеющего один аргумент. Факт railway (novosibirsk, omsk). записан с использованием предиката railway, имеющего два аргумента.

    Число аргументов предиката называется арностью предиката и обозначается black/1 (предикат black имеет один аргумент, его арность равна единице). Предикаты могут не иметь аргументов, арность таких предикатов равна 0.



    1. Переменные

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

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

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

    Пример:

    First_list

    X

    Person

    City

    Переменная, не имеющая значения, называется свободной, переменная, имеющая значение – конкретизированной.

    Как было сказано выше, в Prolog’е нет оператора присваивания, его роль, в некоторых случаях, выполняет оператор равенства =.

    Если записать следующую цель:

    …, X=5, …

    то как эта цель будет рассматриваться, как сравнение или как присваивание, все зависит от того, получила ли какое-либо значение переменная X к моменту доказательства этой цели. Если переменная X имеет значение (например, равное 6), то оператор равенства = работает как оператор сравнения. Если же переменная X свободна (не имеет никакого значения), то оператор равенства = работает как оператор присваивания. При этом совершенно неважно, слева или справа от знака равенства находится имя переменной. Главное, чтобы она была свободной. С точки зрения программы на Prolog’е следующие две цели совершенно одинаковы:

    …, X=5, …

    …, 5=X, …

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

    …, X=X+5, …

    В любом случае такая цель будет ошибочной. Действительно, если переменная X имеет, например, значение равное 10, то предыдущая цель сводится к доказательству цели:

    …, 10=10+5, …

    которая, естественно, не доказывается.

    Если же переменная X свободна, то нельзя к переменной, не имеющей никакого значения, прибавить 5, и присвоить эту неопределенность той же самой переменной. Как же тогда быть, если нужно изменить значение переменной? Ответ один – использовать новое имя, поскольку переменная, которая появляется в тексте программы впервые, считается свободной, и может быть конкретизирована некоторым значением:

    …, Y=X+5, …

    При этом опять же не важен порядок записи. Такая цель также будет правильной, и будет выполнять присваивание (конечно, если переменная X конкретизирована некоторым числом):

    …, X+5=Y, …

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

    Рассмотрим пример:

    parent (“Владимир”, “Михаил”).

    parent (“Владимир”, “Светлана”).

    parent (“Анна”, “Михаил”).

    parent (“Анна ”, “ Светлана”).

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

    Goal: parent (Person, _).

    Решение в данном случае будет избыточным, поскольку есть четыре факта.

    Person=Владимир

    Person=Владимир

    Person=Анна

    Person=Анна

    4 Solutions

    Сравните, если использовать запрос:

    Goal: parent (Person, Child).

    какими будут результаты:

    Person=Владимир, Child=Михаил

    Person=Владимир, Child=Светлана

    Person=Анна, Child=Михаил

    Person=Анна, Child=Светлана

    4 Solutions



    1. Основные секции программы

    Как правило, программа на Prolog’е состоит из нескольких секций, ни одна из которых не является обязательной. Вот основные секции:

    1. DOMAINS – секция описания доменов (типов). Секция применяется только, если в программе используются нестандартные домены.

    2. PREDICATES – секция описания предикатов. Секция применяется, если в программе используются нестандартные предикаты.

    3. CLAUSES – секция предложений. Именно в этой секции записываются предложения: факты и правила вывода.

    4. GOAL – секция цели. В этой секции записывается внутренний запрос.

    На первый взгляд, без секций DOMAINS, PREDICATES и GOAL действительно можно обойтись, но как написать программу без секции CLAUSES? Конечно, такая программа не обладает большим количеством возможностей, но принципиально такую программу написать можно. Например:

    GOAL

    write (“Введите Ваше имя: ”), readln (Name), write (“Здравствуйте, ”, Name, “!”).

    Вот пример программы, состоящей только из секции GOAL. Используются только стандартные домены, следовательно, отпадает необходимость использовать секцию DOMAINS, нет нестандартных предикатов, следовательно, отпадает необходимость использовать секцию PREDICATES, и, наконец, все цели записаны непосредственно в секции GOAL, следовательно, нет необходимости использовать секцию CLAUSES.

    1. Основные стандартные домены

    Доменом в Prolog’е называют тип данных. В Prolog’е, как и других языках программирования, существует несколько стандартных доменов, перечислим их:

    1. integer – целые числа.

    2. real – вещественные числа.

    3. string – строки (любая последовательность символов, заключенная в кавычки).

    4. char – одиночный символ, заключенный в апострофы.

    5. symbol – последовательность латинских букв, цифр и символов подчеркивания, начинающаяся с маленькой буквы или любая последовательность символов, заключенная в кавычки.

    Для примера приведем программу из введения, оформленную по всем правилам:

    PREDICATES

    bird (string)

    has_wings (string)

    can_fly (string)

    can_swim (string)

    CLAUSES

    bird (“журавль”).

    has_wings (“синица”).

    has_wings (“пингвин”).

    can_fly (“синица”).

    can_swim (“пингвин”).

    bird (Object):- has_wings (Object), can_fly (Object).

    GOAL

    bird (Who).

    Несколько замечаний. Поскольку в программе не использовались нестандартные домены, не было необходимости использовать секцию описания доменов DOMAINS. В отличие от примера из введения, где был использован внешний запрос, в данной программе запрос записан в секции GOAL, то есть является внутренним. В таком случае находится только первое решение. Как находить все существующие решения, если используется внутренний запрос, более подробно описано в разделе «Управление поиском с возвратом: предикаты ! и fail»

    1. Поиск с возвратом

    Поиск с возвратом (backtracking) – это один из основных приемов поиска решений поставленной задачи в Prolog’е.

    Каким образом работает поиск с возвратом? Это достаточно хорошо можно пояснить, вот на каком примере.

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

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

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

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

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

    Рассмотрим на примере, каким образом выполняется поиск всех возможных решений с применением поиска с возвратом.

    PREDICATES

    little (symbol)

    middle (symbol)

    big (symbol)

    strong (symbol)

    powerful (symbol)

    CLAUSES

    little (cat).

    little (wolf).

    middle (tiger).

    middle (bear).

    big (elephant).

    big (hippopotamus).

    strong (tiger).

    powerful (Animal):- middle (Animal), strong (Animal).

    powerful (Animal):- big (Animal).

    Итак, обратимся к программе с запросом – какое животное можно назвать мощным?

    Запрос будет выглядеть следующим образом:

    Goal: powerful (Animal).

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

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

    Так как было выбрано первое правило вывода, теперь необходимо последовательно доказать все цели, перечисленные в теле правила. Для доказательства цели middle (Animal) вновь начинается просмотр всех предложений, имеющихся в тексте программы, и находится факт middle (tiger). Но! Поскольку имеется еще один факт, описывающий животное средних размеров middle (bear). , устанавливается вторая точка возврата (назовем ее *2).Переменная Animal получает значение tiger. Первая цель в теле правила успешно доказана.

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

    Так как доказаны все цели в теле правила, считается успешно доказанной головная цель правила, и, следовательно, цель powerful (Animal), записанная в исходном запросе.

    Найдено первое решение: Animal=tiger.

    Поскольку должны быть найдены все возможные решения, вступает в действие поиск с возвратом, который возвращает выполнение программы к последней установленной точке возврата – *2, то есть к цели middle (Animal), которая может быть передоказана. Вновь начинается просмотр всех предложений, но не с самого первого, а с того, на котором была установлена точка возврата *2 и цель middle (Animal) успешно передоказывается фактом middle (bear). Следует отметить, что переменная Animal, получившая при нахождении первого решения значение tiger, потеряла это значение, когда поиск с возвратом вернулся к передоказательству цели middle (Animal), то есть, нет никаких препятствий к тому, чтобы переменная Animal получила теперь значение bear.

    Точка возврата *2 удаляется и вновь не устанавливается, так как нет более фактов middle, описывающих животных средних размеров. Итак, успешно передоказана первая цель в теле правила, и восстанавливается исходный порядок действий, то есть выполняется переход к доказательству второй цели в теле правила, но только теперь это цель strong (bear). Найти факт, доказывающий данную цель, не удается, то есть считается недоказанной вторая цель в теле правила, следовательно, вновь в действие вступает поиск с возвратом и происходит возврат к ближайшей точке возврата, а это точка *1. Точка возврата *1 свидетельствует о том, что вновь начинается просмотр предложений в тексте программы, но не с самого начала, а с предложения, помеченного этой самой точкой *1. При просмотре обнаруживается, что цель в запросе powerful (Animal) может быть передоказана с помощью второго правила вывода.

    Так как выполнен возврат к последней точке возврата, переменная Animal вновь теряет свое значение, и, так как больше возможностей для передоказательства цели в запросе powerful (Animal) нет, точка возврата *1 более не устанавливается.

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

    Итак, цель запроса powerful (Animal) была сопоставлена с заголовком второго правила, что привело к необходимости доказать единственную цель в теле правила – big (Animal). Для этого вновь начинается просмотр предложений в тексте программы с самого начала и обнаруживается факт big (elephant). Вновь устанавливается точка возврата, назовем ее *3, говорящая о том, что цель big (Animal) в дальнейшем может быть передоказана. Факт big (elephant). успешно доказывает цель big (Animal) и переменная Animal принимает значение elephant. Так как успешно доказаны все (в данном случае одна) цели в теле правила, считается успешно доказанной и заголовочная цель правила, что приводит к успеху в доказательстве цели в запросе, и вот оно, второе решение:

    Animal=elephant.

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

    Animal=hippopotamus.

    Так как все возможные решения найдены, выполнение программы заканчивается.

    Далее приводится пример трассировки (пошагового выполнения) только что рассмотренного примера.

    Условные обозначения для трассировки:

    1. CALL – цель, которую нужно доказать.

    2. RETURN – цель, которая успешно доказана.

    3. REDO – поиск с возвратом.

    4. FAIL – неудача в доказательстве.

    5. * – точка возврата.

    6. _ – переменная, не имеющая значения.



    CALL: powerful (_)

    CALL: middle (_)

    RETURN: *middle ("tiger")

    CALL: strong ("tiger")

    RETURN: strong ("tiger")

    RETURN: *powerful ("tiger")

    REDO: middle (_)

    RETURN: middle ("bear")

    CALL: strong ("bear")

    FAIL: strong ("bear")

    REDO: powerful (_)

    CALL: big (_)

    RETURN: *big ("elephant")

    RETURN: powerful ("elephant")

    REDO: big (_)

    RETURN: big ("hippopotamus")

    RETURN: powerful ("hippopotamus")

    Теперь можно сформулировать основные правила поиска с возвратом:

    1. Цели должны быть доказаны по порядку, слева, направо.

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

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

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

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

    Пример дерева целей для ранее рассмотренного примера, для случая нахождения первого решения Animal=tiger.

    Условные обозначения в дереве целей:

    1. powerful (Animal) – цель, которую нужно доказать.
      1   2   3   4   5


    написать администратору сайта