Программирование на языке ПРОЛОГ для искуственного интеллекта

       

Работа с базой данных


Реляционная модель предполагает, что база данных - это описание некоторого множества отношений. Пролог-программу можно рассматривать как именно такую базу данных: описание отношений частично присутствует в ней в явном виде (факты), а частично - в неявном (правила). Более того, встроенные предикаты дают возможность корректировать эту базу данных в процессе выполнения программ. Это делается добавлением к программе (в процессе вычисления) новых предложений или же вычеркиванием из нее уже существующих. Предикаты, используемые для этой цели, таковы: assert (добавить), asserta, assertz и retract (удалить).

Цель

        assert( С)

всегда успешна, а в качестве своего побочного эффекта вызывает "констатацию" предложения   С,  т. е. добавление его к базе данных. Цель

        retract( С)

приводит к противоположному эффекту: удаляет предложение, сопоставимое с  С. Следующий диалог иллюстрирует их работу:

        ?-  кризис.
        no

        ? -  assert( кризис).
        yes

        ?-  кризис.
        yes

        ? -  retract( кризис).
        yes



        ?-  кризис.
        no

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

        хорошая :-
                солнечно, not дождь.


        необычная :-
                солнечно, дождь.


        отвратительная :-
                дождь, туман.


        дождь.

        туман.

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

        ?-  хорошая.
        no


        ?-   отвратительная.
        yes


        ?-  retract( туман).
        yes


        ?-   отвратительная.
        no


        ?-  assert( солнечно).
        yes


        ?-  необычная.
        yes


        ?-  retract( дождь).
        уes


        ?-  хорошая.
        yes


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

        быстр( энн).
        медл( том).
        медл( пат).


К этой программе можно добавить правило:

        ?-  assert(


                         ( быстрее( X, Y) :-
                              быстр( X), медл( Y) ) ).


        yes

        ?-  быстрее( А, В).
        А = энн
        В = том


        ?-  retract( медл( X) ).
        Х = том;
        X = пат;
        nо


        ?-  быстрее( энн, _ ).
        nо


Заметьте, что при добавлении нового правила синтаксис требует, чтобы оно (как аргумент assert) было заключено в скобки.

При добавлении нового предложения может возникнуть желание указать, на какое место в базе данных его следует поместить. Такую возможность обеспечивают предикаты asserta и assertz. Цель

        asserta( С)

помещает С в начале базы данных. Цель

        assertz( С)

- в конце. Вот пример, иллюстрирующий работу этих предикатов:

        ?-  assеrt( р( a)), assertz( р( b) ), asserta( p( c) ).

        yes

        ?-  p( X).

        Х = с;
        Х = а;
        Х = b


Между consult и assertz существует связь. Обращение к файлу при помощи consult можно в терминах assertz определить так: считать все термы (предложения) файла и добавить их в конец базы данных.

Одним из полезных применений предиката asserta является накопление уже вычисленных ответов на вопросы.


Пусть, например, в программе определен предикат

        решить( Задача, Решение)

Мы можем теперь задать вопрос и потребовать, чтобы ответ на него был запомнен, с тем чтобы облегчить получение ответов на будущие вопросы:

        ?-  решить( задача1, решение),
             asserta( решить( Задача1, Решение) ).


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

Развитие этой идеи состоит в использовании assert для порождения всех решений в виде таблицы фактов. Например, создать таблицу произведений всех чисел от 0 до 9 можно так: породить пару чисел Х и Y, вычислить Z, равное Х * Y, добавить эти три числа в виде строки в таблицу произведений, а затем создать искусственно неуспех. Неуспех вызовет возврат, в результате которого будет найдена новая пара чисел, и в таблицу добавится новая строка и т.д. Эта идея реализована в процедуре

        таблица :-
                L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],

                принадлежит( X, L),            % Выбрать первый сомножитель
                принадлежит( Y, L),            % Выбрать второй сомножитель


                Z is X*Y,
                assert( произв( X,Y,Z) ),
                fail.


Вопрос

            ?-   таблица.

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

        ?-  произв( А, В, 8).

        А = 1
        В = 8;


        А = 2
        В = 4;


        . . .

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


Содержание раздела