При вводе и выводе информации в Прологе используется понятие потоков. Файлы для чтения - это входные потоки. Файлы для записи - это выходные потоки.
Для пользователя определены два потока:
· информация вводимая с клавиатуры - входной поток.
· информация выводимая на монитор - выходной поток.
Эти потоки являются псевдофайлами с именем user.
В каждый момент времени для Пролога активны два файла:
для ввода - текущий входной поток.
для вывода - текущий выходной поток.
Обработка входных потоков
Входные потоки Пролог "видит". Поэтому определены следующие предикаты:
|
Обработка выходных потоков
В выходные потоки Пролог "говорит".
| |
Пример:
|
|
P3:- tell('a.dat'),write(a),told,write(a).
Обработка символов
Пролог имеет несколько предикатов для обработки символов.
put(X). Символы записываются в текущий выходной поток при помощи предиката put(C), где C - символ, который нужно вывести, представленный в кодировке ASCII (число от 0 до 127).
Пример:
P4:-put(65),put(66),put(67).
?-p4.
A B C
get(X). Печатный символ (код по ASCII больше 31) читается предикатом get(X). Аргумент X принимает числовое значение. Непечатный символ игнорируется.
Пример:
P4:- get (A), get (B), get (C).
?-p4.
65 66 67
get0(X) читает любой (печатный или нет) символ.
Предикат repeat - цель, которая всегда успешна. Ее особое свойство состоит в том, что она всегда находит альтернативу. Поэтому всякий раз, когда до нее доходит перебор, она порождает новую ветвь вычислений.
Хотя repeat встроенный предикат, он может быть определен следующим образом:
repeat. repeat:- repeat. | ||
Применение repeat можно посмотреть на примере процедуры sq, которая читает последовательность чисел и выдает их квадраты.
Последовательность чисел заканчивается атомом stop.
Рассмотрим предикаты, которые дают положительный ответ в случае соответствия переменной ее назначению:
- atom(X) - да, если X -атом.
- integer(X) - да, если X -целое.
- atomic(X) - да, если X -целое или атом.
- var(X) - да, если X -не конкретизированная переменная.
- nonvar(X) - да, если X - терм, отличный от переменной, или уже конкретизированная переменная.
Рассмотрим в качестве примера универсальной предикат суммирования:
|
|
Примеры:
?-plus(1,2,Z). Z=3.?-plus(X,2,4). X=2.?-plus(1,Y,2). Y=1.?-plus(1,2,4). no. | ||
Метапредикаты. Встроенные предикаты обработки термов
Т=..L (читается "univ") истина, если L- список, начинающийся с главного функтора терма T, вслед за которым идут его аргументы. | |
Примеры:
?-f(a,b)=..L.
L=[f,a,b]
?-T=..(like,tom,mary).
T=like(tom,mary).
functor(Term,F,N) будет истиной, если F - главный функтор
терма Term,a N -количество его аргументов. Он позволяет из списка получать структуру и наоборот.
Примеры:
?-functor(f(a,S),F,G).
F=f
G=2
?-functor(b,F,G).
F=b
G=0
?-functor(F,d,4).
F=d(_,_,_,_).
arg(N,T,A) - обеспечивает доступ к конкретному аргументу структуры, где
N - номер аргумента, T - терм, A - значение аргумента.
Пример:
?-arg(2,f(a,b),X)
X=b
?-F=..[a,2,3,4,5],arg(4,F,X).
X=5
Предикаты работы с базой данных
Программа в Прологе рассматривается, какбаза данных. Можно добавлять к базе данных новые предложения и удалять старые предложения.
Аналогично, в ходе выполнения программы ее можно изменять.
Для этого используются специальные предикаты.
- assert(C) - добавляет к базе данных предложение С.
?-assert(a(b)).
?-listing.
a(b).
?-assert(a(a)).
?-assert(a(c)).
?-listing.
a(a).
a(c).
В базу данных можно добавлять целые предложения:
assert((a(X):-X>0))
?-F=..[a,b,c],assert(F).
a(b,c).
·
assert используется иногда для сохранения промежуточных результатов.
Может служить для передачи значений переменных между предложениями.
Использование assert лучше ограничивать из-за усложнения понимания программы.
Также на практике используется команда обратная assert, а именно retract(C), которая удаляет предложения, согласуемые с C.
Поиск в лабиринте
Рассмотрим следующую задачу и ее решение средствами языка Пролог.
Существует дом с комнатами b, c, d, e, f, g. Между некоторыми комнатами есть двери. В одной из комнат (g) находится клад. Требуется пройти через комнаты к кладу.
Запишем информацию о дверях:
door(a,b). door(b,c). door(b,e). door(d,e). door(d,c). door(e,g). door(e,f). | |||
Переход из комнаты в комнату будет производиться через предикат:
Path(X,Y,T)
- X - исходная комната;
- Y - конечная комната;
- T - список пройденных комнат. Он необходим, чтобы не проходить дважды через одну комнату.
Введем терминальное (целевое) условие:
Path(Y,Y,T)
Т.е. мы находимся в нужной комнате.
Правило перехода из комнаты в комнату можно записать следующим образом:
path(X,Y,T):-door(X,Z),not(member(Z,T)), path(Z,Y,[Z|T]).Характер рекурсии другой. Один аргумент всегда увеличивается. В этой программе каждая дверь в одну сторону. Чтобы избежать этого, есть два варианта:
Поиск пути в комнату f:
?-path(a,f,[]).
Добавим факт о кладе:
Money(g).
Тогда найдем путь к комнате с кладом.
Цель- запрос будет выглядеть так:
?-path(a,X,[]), money(X). | ||||
Это вопрос типа "создать и проверить" находит достижимые комнаты, а затем проверяет наличие в них клада.
Запрос вида:
?-money(X),path(a,X,[]). | ||||
требует сначала найти комнату, а затем путь к ней.
Задачи для самостоятельного решения:
- Как вывести путь T?
- Как найти самый короткий путь?