Предикаты read и write
Встроенный предикат read используется для чтения термов из текущего входного потока, а цель read] X)
вызывает чтение следующего терма, Т, и согласование этого терма с X. Если X представляет собой переменную, то в результате X становится конкретизированной значением Т, а если согласование оканчивается неудачей, цель read { X) не достигается. Предикат read является детерминированным, поэтому в случае неудачи не выполняется перебор с возвратами для ввода другого терма. За каждым термом во входном файле должны следовать точка и пробел (или символ с обозначением конца строки).
Если предикат read | X; вызывается на выполнение после достижения конца текущего входного файла, то переменная х становится конкретизированной значением атома end_of_file.
Встроенный предикат write выводит терм, поэтому цель
write ( X!
выводит терм X в текущий выходной файл. Терм X выводится в такой же стандартной синтаксической форме, в которой Prolog обычно отображает значения переменных. Полезным средством языка Prolog является то, что процедура write "умеет" отображать любые термы, независимо от того, насколько они могут быть сложными.
Как правило, в любой реализации Prolog предусмотрены дополнительные встроенные предикаты форматирования вывода, которые вставляют в выходной поток пробелы и символы с обозначением новой строки. Например, цель
tab[ К)
обеспечивает вывод N пробелов, а предикат п] (который не имеет параметров) обеспечивает вывод символа с обозначением конца строки.
Применение этих процедур иллюстрируется в приведенных ниже примерах.
Предположим, что используется следующая процедура, которая вычисляет куб любого числа: cube{ Ы, С) :-С is Л * И * N.
Допустим, что необходимо использовать ее для вычисления кубов ряда чисел. Такую задачу можно выполнить с помощью приведенной ниже последовательности вопросов.
?- cube(2,X).
?- cube ( 5, Y) . Y - 125
?- cube[12, Z) . Z = 1728
Для получения каждого числа в данном случае приходилось вводить соответствующую цель, поэтому модифицируем программу таким образом, чтобы процедура cube могла сама считывать данные. Теперь программа будет считывать данные И выводить значения соответствующих им кубов до тех пор, пока не будет считан атом stop:
cube : -read: X), process ( X) .
orocess{ stop! :- !. process ( N) :-
Глава 6. Ввод и вывод
С is N N И, write [ С), cube.
Это - наглядный пример программы, декларативное значение которой сформулировать почти невозможно. Но ее процедурное значение является несложным: чтобы выполнить процедуру cube, вначале следует прочитать переменную X, а затем ее обработать; если X = stop, то работа закончена, в противном случае необходимо вывести куб X и рекурсивно вызвать процедуру cube для дальнейшей обработки данных. Таблицу кубов чисел можно подготовить с использованием этой новой процедуры следующим образом:
?- cube.
2.
-
5.
12.
stop, yes
Пользователем на терминале были введены числа 2, 5 и 12; остальные числа выведены программой. Обратите внимание на то, что за каждым числом, введенным пользователем, должна следовать точка, которая обозначает конец терма.
На первый взгляд может показаться, что есть возможность упростить приведенную выше процедуру cube. Но следующая попытка упрощения является неправильной:
cube : -
read! stop) , !.
cube : -read [ N),
С is N * N * N,write ( C), cube.
Причину, по которой эта программа не работает должным образом, можно легко обнаружить, выполнив трассировку программы с входными данными, допустим 5. После чтения этого числа цель reacH stop) не будет достигнута и введенное число безвозвратно потеряется. Следующая цель read обеспечит ввод очередного терма. С другой стороны, может оказаться, что сигнал stop считан целью read ( N), а это в дальнейшем вызовет попытку перемножить нечисловые данные.
Процедура cube обеспечивает взаимодействие пользователя и программы. В подобных случаях обычно желательно, чтобы программа перед чтением новых данных с терминала сообщала пользователю, что она готова принять информацию и, возможно, даже показывала, какого рода информацию она ожидает. Такая задача обычно решается путем отправки пользователю перед чтением данных определенного сигнала в виде так называемого приглашения. Рассматриваемая процедура cube может быть откорректирована соответствующим образом, например, как показано ниже. cube :-
writef "Uext item, please: '], read(x) ,
process ! X) .
process! stop) ;- !. process{ N) :-
С is И • N ' N,
write ( 'Cube of ' ) , write! N) , write! ' is ' ) ,
write! C) , nl,
cube.
В таком случае диалог с новой версией процедуры cube может происходить, например, следующим образом:
140 Часть I. Язык Prolog
?- cube.
(text Itea. please: 5.
Cube of 5 is 125
Next item, please; 12.
Cube of 12 is 1728
next item, please: stop.
yes
В зависимости от реализации Prolog, после вывода приглашения может потребоваться дополнительный запрос (скажем, такой как ttyf lush), который вынуждал бы приглашение фактически появляться на экране перед чтением.
В следующих разделах рассматриваются некоторые типичные примеры операций, связанных с чтением и записью.