Предикаты 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), который вынуждал бы приглашение фактически появляться на экране перед чтением.

В следующих разделах рассматриваются некоторые типичные примеры операций, связанных с чтением и записью.