
- •7. Списки в Прологе
- •Определение и унификация
- •Упражнения на унификацию
- •Рассмотрим ещё несколько примеров
- •7.2 Операции на списках
- •7.2.1 Поиск элемента в списке
- •7.2.2 Создание списков
- •7.2.2.1 Простейший случай
- •7.2.2.2 Получение списка из двух существующих списков
- •7.2.2.3 Получение списка по условию
- •7.2.2.4 Вычеркивание элементов списка
- •7.2.2.5 Замена элементов списка
- •7.2.2.6 Проверка, является ли аргумент списком.
- •7.2.2.7 Подсчет количества элементов в списке.
- •7.2.2.9 Вывод списка на дисплей.
- •7.3 Задачи на списках
- •7.3.1 Нахождение путей в связанном неориентированном графе
- •7.3.2 Синтаксический анализатор на дка
7.3.2 Синтаксический анализатор на дка
С
оставим
программу, реализующую работу произвольного
детерминированного конечного автомата
(ДКА). Она будет проверять входную в ДКА
последовательность символов, и отвечать
на запрос, принадлежит ли эта цепочка
языку конкретного автомата.
Пусть автомат задан такой диаграммой состояний:
Тогда регулярное выражение, описывающее его язык, будет:
b* a a* b (a|b)*
Определим для него функцию переходов X,w)=Y. Это будет множество фактов вида delta(X,w,Y) (функция перехода из состояния X в состояние Y по входному символу w)
Выпишем все эти факты, что образуют заданный ДКА:
delta(0,a,1).
delta(0,b,0).
delta(1,a,1).
delta(1,b,2).
delta(2,a,2).
delta(2,b,2).
Необходимо также обозначить начальное и конечное состояние ДКА:
start(0).
final(2).
Итак, основное предложение программы:
parse(String) :- start(S), trans(S,String).
где:
String – это список, состоящий из символов входной цепочки.
start(S)- начальное состояние ДКА.
trans(S, String)- это во-первых предикат, а во вторых он должен находить (и попутно выдать на экран) последовательность символов, которая приводит процесс переходов из начального состояния в конечное.
Само собой, конечным состоянием будет 2. Но надо ещё учесть, что в него мы должны прийти с пустой цепочкой входных символов. Это условие лучше всего записать в общем виде:
trans(X,[]):- final(X),
write(X),
write(' '),
write([]), nl.
В любом другом случае нам нужно указать текущее состояние, первый не разобранный символ и необработанную цепочку ввода..
trans(X,[A|String]) :-
delta(X,A,Y), % Возможный переход из текущего состояния X по символу A
write(X), % Выводим текущее состояние
write(' '),
write([A|String]), % String – это цепочка, которая осталась
nl, % после принятия символа A
trans(Y,String). % Продолжение разбора, начиная с Y
И теперь посмотрим его работу на конкретных цепочках.
?- parse([b,b,a,a,b,a,b]).
0 [b,b,a,a,b,a,b]
0 [b,a,a,b,a,b]
0 [a,a,b,a,b]
1 [a,b,a,b]
1 [b,a,b]
2 [a,b]
2 [b]
2 []
true
?- parse([b,b,a]).
0 [b,b,a]
0 [b,a]
0 [a]
false.
?- parse([a,a,b]).
0 [a, a, b]
1 [a, b]
1 [b]
2 []
true
Концепция списков позволяет решать на Прологе довольно сложные задачи.