Опубликован: 15.06.2007 | Уровень: специалист | Доступ: платный
Лекция 7:

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

< Лекция 6 || Лекция 7: 123 || Лекция 8 >

Структуры данных

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

Списки
СПИСКОВАЯ ФОРМА ЗАПИСИ

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

[джек, джон, фред, джилл, джон]

[имя (джон, смит), возраст (джек, 24), X]

[Х.У.дата (12,январь, 1986) ,Х]

[]

Запись [H|T] определяет список, полученный добавлением Н в начало списка Т. Говорят, что Н — голова, а Тхвост списка [HIT]. На вопрос

?-L=[a | [b, c, d]]. будет получен ответ L=[a, b, c, d]

а на запрос ?-L= [a, b, c, d], L2=[2 | L]. — ответ L=[a, b, c, d], L2- [2, a, b, c, d]

Запись [Н | Т] используется для того, чтобы определить голову и хвост списка. Так, запрос ?- [X | Y]=[a, b, c]. дает Х=а, Y=[b, c]

Заметим, что употребление имен переменных Н и Т необязательно. Кроме записи вида [H|T], для выборки термов используются переменные. Запрос

?-[a, X, Y]=[a, b, c].

определит значения

X=b Y=c

а запрос

?- [личность(Х) | Т]=[личность(джон), а, b].

значения

Х=джон Т=[а, Ь]

НЕКОТОРЫЕ СТАНДАРТНЫЕ ЦЕЛЕВЫЕ УТВЕРЖДЕНИЯ ДЛЯ ОБРАБОТКИ СПИСКОВ

Покажем на примерах, как можно использовать запись вида [Н | T] вместе с рекурсией для определения некоторых полезных целевых утверждений для работы со списками.

Принадлежность списку. Сформулируем задачу проверки принадлежности данного терма списку.

Граничное условие:

Терм R содержится в списке [H|T], если R=H.

Рекурсивное условие:

Терм R содержится в списке [H|T], если R содержится в списке Т.

Первый вариант записи определения на Прологе имеет вид:

содержится (R, L) :-
L=[H I T],
H=R. 
содержится(Р, L) :-
L=[H|T],
содержится (R, T).

Цель L=[H I T] в теле обоих утверждений служит для того, чтобы разделить список L на голову и хвост.

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

принадлежит (R, [R | Т]). принадлежит (R, [H | Т]) :- принадлежит (R, T).

На запрос

?- принадлежит(а, [а, Ь, с]).

будет получен ответ

да

на запрос

?- принадлежит(b, [a, b, с]).

- ответ

да

но на запрос

?- принадлежит(d, (a, b, c)).

Пролог дает ответ

нет

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

Соединение двух списков. Задача присоединения списка Q к списку Р, в результате чего получается список R, формулируется следующим образом:

Граничное условие:

Присоединение списка Q к [] дает Q.

Рекурсивное условие:

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

Определение можно непосредственно написать на Прологе:

соединить([],0,0). 
соединить(Р,Q,Р) :-
Р=[НР | ТР],
соединить(TP, Q, TR),
R=[HP | TR].

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

присоединить([] ,Q,Q). 
присоединить(HP | TP], Q, [HP | TR]) :-
присоединить (TP, Q, TR).

На запрос

?- присоединить [а, b, с], [d, e], L).

будет получен ответ

L = [a, b, c, d].

но на запрос

?- присоединить([a, b], [c, d], [e, f]).

ответом будет

нет

Часто процедура присоединить используется для получения списков, находящихся слева и справа от данного элемента:

присоединить (L [джим, р], [джек,.билл, джим, тим, джим, боб] ) .
L = [джек, билл]
R = [тим, джим, боб]
другие решения (да/нет)? да
L=[джек, билл, джим, тим]
R=[боб]
другие решения (да/нет)? да
других решений нет

Индексирование списка. Задача получения N -ro терма в списке определяется следующим образом:

Граничное условие:

Первый терм в списке [Н | Т] есть Н.

Рекурсивное условие:

N-й терм в списке [Н | Т] является (N-I)-м термом в списке Т.

Данному определению соответствует программа:

/* Граничное условие:
получить ([H | Т], 1, Н). /* Рекурсивное условие:
получить([Н | Т], N, У) :-
М is N - 1, 
получить (Т, М ,Y).

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

bagof(X,Y,L) — определяет список термов L, конкретизирующих переменную Х как аргумент предиката Y, которые делают истинным предикат Y

setof(X,Y,L) — все сказанное о предикате bagof относится и к setof, за исключением того, что список L отсортирован и из него удалены все повторения.

Если имеются факты:

собака(рекс). 
собака (голди). 
собака (фидо). 
собака(реке).

то на запрос

?- bagof(D, co6aкa(D), L),

будет получен ответ

L=[реке, голди, фидо, рекс]

в то время как

?-setof(D, co6aкa(D), L). дает значение

L=[фидо, голди, рекc]

Пример: сложение многочленов

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

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

Р(х)=3+3х-4х^3+2х^9

Q(х)=4х+х^2-3х^3+7х^4+8х^5

Заметим, что каждое подвыражение (такое, как Зх ^3, Зх, 3 ) имеет самое большее две переменные компоненты: число, стоящее перед х, называемое коэффициентом, и число, стоящее после ^ - степень. Следовательно, подвыражение представляется термом

х(Коэффициент, Степень)

Так, 5х^2 записывается как х(5,2), х^З представляется как х(1,3), а поскольку х^0 равно 1, подвыражению 5 соответствует терм х(5,0).

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

[x(3, 0), '+', x(3, l), '-', x(4, 3), '+', x(2, 9)]

Воспользуемся тем, что многочлен

3 + 3х - 4х^3 + 2х^9

допускает замену на эквивалентный

3 + 3х + (-4)х^3 + 2х^9 Тогда он выражается списком:

[х(3, 0), '+', х(3, 1), '+', х(-4, 3), '+', х(2, 9)]

В такой записи между термами всегда стоят знаки '+'. Следовательно, их можно опустить, и многочлен принимает окончательный вид:

[х(3, 0), х(3, 1), х(-4, 3), х(2, 9)]

Подразумевается, что между всеми термами списка стоят знаки '+'. Представлением многочлена Q(x) будет

[х(4, 1), х(1, 2), х(-3, 3), х(7, 4), х(8, 5)]

Сложение многочленов. Теперь напишем целевые утверждения для сложения двух многочленов. Сложение многочленов

3-2х^2+4х^3+6х^6

-1+3х^2-4х^3

в результате дает

2+х^2+6х^6

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

Сложение многочлена Р с многочленом Q осуществляется следующим образом.

Граничное условие:

Р, складываемый с [], дает Р.

[], складываемый с Q, дает Q.

Рекурсивное условие:

При сложении Р с Q, в результате чего получается многочлен R, возможны 4 случая:

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

    Р(х)=3х^2+5х^3

    Q(x)=4x^3+3x^4

    то первый терм R(x) равен 3х^2 (первому терму в Р(х) ). Хвост R(x) равен 9х^3+3х^4, т.е. результату сложения Q(x) и хвоста Р(х) ;

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

    Р(х)=2х^3+5х^4

    Q(x)=3x^3-x^4

    то первый терм R(x) равен 3х^3 (первому терму в Q(x) ), а хвост R(x) равен 2х^3+4х^4 (результату сложения Р(х) и хвоста Q(x) );

  3. степени первых термов в Р и Q равны, а сумма их коэффициентов отлична от нуля. В таком случае первый терм в R имеет коэффициент, равный сумме коэффициентов первых термов в Р и Q. Степень первого терма в R равна степени первого терма в Р (или Q ). Хвост R получается при сложении хвоста Р и хвоста Q. Например, если Р и Q имеют вид

    Р(х)=2х+3х^3

    Q(x)=3x+4x^4

    то первый терм многочлена R (х) равен (результату сложения первого терма в Р(х) с первым термом в Q(x) ). Хвост R(x) равен 3х^3+4х^4 (результату сложения хвоста Р(х) и хвоста Q(x) );

  4. степени первых термов в Р и Q одинаковы, но сумма коэффициентов равна нулю. В данном случае многочлен R равен результату сложения хвоста Р с хвостом Q. Например, если

    р(х)=2+2х

    Q(x)=2-3x^2

    то

    R(x)=2x-3x^2

    (это результат сложения хвостов многочленов Р (х) и Q (х) ).

Рассмотренный процесс сложения многочленов можно непосредственно записать на языке Пролог.

/* Граничные условия 
слож_мн([], Q Q).
слож_мн(P, [], P).
/* Рекурсивное условие
/* (a)
слож_мн([x(Pc, Pp)|Pt], [x(Qc, Qp)|Qt], 
[x(Pc,Pp)IRt]) :-
PpQp,
слож_мн(Рt, [х(Qс,Qр) | Qt], Rt). 
/*(б)
слож_мн([x(Pc, Pp) | Pt], [x(Qc, Qp) | Qt], 
[x(Qc, Qp) | Rt]) :-
PpQp,
слож_мн([x(Pc, Pp) | Pt], Qt, Rt). 
/*(в)
слож_мн([x(Pc, Pp) | Pt], [х(Qc,Pp) | Qt], 
[x(Rc, Pp) | Rt]) :-
Rc is Pc+Qc,
Rc =\= 0,
слож_мн(Pt, Qt,Rt). 
/*(r) 
слож_мн([х(Рс, Рр) | Pt], 
[x(Qc.Pp) | Qt], Rt) :-
Re is Pc+Qc,
Rc =:= 0,
слож_мн(Pt, Qt, Rt).

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

Списки как термы. В начале лекции мы упомянули о том, что список представляется с помощью терма. Такой терм имеет функтор '.', два аргумента и определяется рекурсивно. Первый аргумент является головой списка, а второй — термом, обозначающим хвост списка. Пустой список обозначается []. Тогда список [а, b] эквивалентен терму. (а,.(b, [])).

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

[[a, b], [c, d], [a], a]

есть правильно записанный список, и на запрос

?- [Н | Т]=[[а, b], с].

Пролог дает ответ

Н=[а, b] Т=[с]

< Лекция 6 || Лекция 7: 123 || Лекция 8 >
Сергей Пчеляков
Сергей Пчеляков

Добрый день!

В курсе "Проектирование систем искусственного интеллекта" начал проходить обучение и сдал тесты по лекциям 1,2,3,4. Но видимо из-за того что не записался на курс, после того как записался на курс у меня затерлись результаты сданных тестов. Можно как-то исправить (восстановить результаты по тестам 1,2,3,4) ?

Виктор Рубцов
Виктор Рубцов
Музафар Холаев
Музафар Холаев
Россия
Александр Останин
Александр Останин
Россия, Лысьва