|
Лекция
4 |
|
|
|
Списки. Встроенные предикаты. |
|
|
4.1.1 Представление
списка диаграммой
4.1.2 Выделение головы и
хвоста списка
4.1.4 Определения
отношений через cons форму списка
4.2 Процедуры обработки
списков
4.3.1 Простые встроенные
предикаты ввода-вывода.
4.3.2 Процедурный смысл
встроенных предикатов ввода-вывода.
4.4.1 Ввод-вывод списка
как терма.
4.4.2 Поэлементный
ввод-вывод списка.
Списки - такая же важная структура данных в Прологе,
как и в Лиспе.
Список в Лиспе (a b c d) (1 2 (3))
записывается на Прологе [a,b,c,d] [1,2,[3]]
т.е. элементы записываются в
квадратных скобках через запятую.
Элементами списка могут быть любые термы.
Пустой список - не nil , а [ ].
Список в лиспе можно представить через
функцию cons
В Прологе функции cons соответствует функтор "." (точка).
.(a,[]) соответствует [a] , это другая форма
записи
или
Ответственно список [a,b,c] представляется как структура .(а
,.(b,.(c,[])
или в виде дерева, или диаграммой "виноградная лоза"
|
|
|
В лиспе : |
|
Для вложенных списков [a,b,[c,d]]
|
- на верхнем уровне три элемента - на втором уровне два элемента. |
|
|
Главной операцией при работе |
|
В Лиспе для этого используются функции
car и cdr.
В Прологе имеется специальная форма
представления списка, называемая cons-формой записи:
[Head|Tail] или [H|T] [a|[]] = [а]
При конкретизации формы списком H
сопоставляется с головой списка, а Т - с хвостом.
Например
|
р([a,b,c]). |
|
?-p([X|Y]). |
|
|
|
|
Таким образом выделяются одновременно
голова списка и хвост.
Рассмотрим сопоставление двух списков
:
|
|
Шаблон (образец)
списка -это форма описания
множества (семейства)списков, обладающих определенными свойствами.
Например:
Шаблон списка [X|Y] описывает любой список,состоящий не менее чем
из одного элемента.
Шаблон [X,Y|Z] - список, состоящий не менее чем из двух
элементов.
Шаблон [b|Z] - список, первым элементом которого является
b.
Шаблон [Y,X,Z] - список из трех элементов.
Шаблоны списка используются при описании
процедур работы со списками.
Задача 1: Определить отношение replace_first,
которое заменяет первый элемент списка новым
Например
|
?-replace_first([a,b,с],w,X).
|
|
|
|
|
Это отношение:
|
replace_first([H|T],A,[A|T]). или |
Что будет ответом для следующего
вопроса ?
|
?-replace_first([_|T],A,[a,b,c]).
|
|
|
|
|
Процедура в прологе - это совокупность предложений с головами,
представленными одинаковыми термами.
Для обработки списков используются
типовые процедуры, аналогичные функциям лиспа.
Проверяет принадлежность элемента
списку.
|
member(X, L) |
Если X принадлежит L, то
истина и ложь в противном случае.
С точки зрения декларативного смысла:
Можно записать:
|
member(X, [X|T]). |
С точки зрения процедурного смысла -
это рекурсивная процедура.
Первое предложение терминальное
условие.
Когда хвост будет равен [] проверка остановиться.
Второе предложение рекурсивное.
Сокращение списка происходит за счет взятия хвоста (cdr-рекурсия).
Примеры применения
|
?-member(a, [a, b,
с]). |
|
|
|
|
Ответьте на вопрос:
|
?-member(a, X). |
|
|
|
|
Используется для соединения двух
списков. т.е
|
append (L1, L2, L3) |
L1 и L2 - списки, а L3 - их
соединение.
|
?-append ([a, b],
[c], [a,b,c]). |
|
|
|
|
Для определения процедуры append
используем два предложения:
|
append([], L, L). |
|X| L1 | L2 | ---------------------------------------- |X| L3 | |
С точки зрения процедурной семантики
первое предложение - терминальное условие,
второе - рекурсивное с хвостовой рекурсией.
Рассмотрим примеры применения
|
?-append([a], [b,
c], L). ?-append([a], L, [a, b, c]). ?-append(L, [b, c], [a, b, c]). |
|
|
|
|
Можно использовать для разбиения
|
?-append(L1, L2, [a,
b, c]). |
|
|
|
|
Процедуру арреnd можно
использовать для поиска комбинаций элементов.
Например можно выделить списки слева и справа от элемента
|
?-append(L, [3|R],
[1, 2, 3, 4, 5]). |
|
|
|
|
Можно удалить все, что следует за
данным элементом и этот элемент тоже:
|
?-L1=[a, b, c, d,
e], append(L2, [c|_], L1). |
|
|
|
|
Можно определить процедуру ,выделяющую
последний элемент в списке:
|
last(X, L):-append(_, [X], L). |
Процедура reverse обращает
список.
|
reverse([], []). |
|
reverse([X|L1], L2):-reverse(L1, L3), |
Можно, используя рекурсивный вызов,
легко посчитать длину списка:
|
length([], 0). |
|
?-length([a, b, c],
N). |
|
|
|
|
До сих пор мы получали ответы в форме
предлагаемой прологом:
Однако можно задавать вопросы и
получать ответы в произвольной форме.
Для этого достаточно использовать т.н.
встроенные предикаты.
Встроенные предикаты - предикаты исходно определенные в прологе, для
которых не существует процедур в базе данных.
Когда интерпретатор встречает цель,
которая сравнивается с встроенным предикатом, он вызывает встроенную процедуру.
Встроенные предикаты обычно выполняют
функции не связанные с логическим выводом.
При сопоставлении строенные предикаты
обычно дают побочный эффект,который не устраняется при бэктрекинге.
Встроенные предикаты обеспечивают
возможности ввода-вывода информации:
Например,
|
pr1:- read(X),nl,write('X='),tab(2),write(X). |
При вызове
|
?-pr1. |
|
|
|
|
последовательность термов читает
значение X,переводит строку,печатает 'X=',пропускает два пробела
и печатает значение X.
Определяя встроенные предикаты мы
писали: "этот предикат всегда успешен. Когда вызывается, то побочным
эффектом будет... При бэктрекинге предикат дает неудачу. Бэктрекинг не
сбрасывает побочный эффект."
Обычно в прологе вход в цель возможен
через "call" при вызове и через "redo" при
бэктрекинге.
Цель имеет внутреннею структуру:
|
Первый ромб - решение при вызове call. |
Для встроенных предикатов нет
внутренних точек решения внутри цели.
Она представляется в виде:
|
Таким образом проход через встроенный предикат будет : |
Для ввода-вывода списков возможны
следующие два способа.
При этом способе список
рассматривается как один терм.
Например, процедура:
|
pr:-write('Введите список L:'),nl,
|
При вызове цели
|
?-pr. |
|
|
|
|
она будет выполнятся следующим
образом:
Введите список L:
[a,b,c,d].
Список L= [a,b,c,d]
Данный способ может быть организован с
помощью рекурсивно определенных процедур.
|
read_list([X|T]):-write('Введите элемент: '),
|
end - терм , означающий конец списка.
|
read_list([]).
|
Тогда после вызова цели:
|
?-read_list(L),nl,write('Список='),nl,
write_list(L). |
|
|
|
|
Возникает следующий диалог:
Введите элемент: a.
Введите элемент: b.
Введите элемент: c.
Введите элемент: d.
Введите элемент: end.
Список= a b c d
(c) M.N.Morozov, 1999.