Стили и методы программирования
4cab9ef0

Организация вычислений и ввода-вывода


Во многих случаях даже в поисковой программе необходимо производить вычисления. В языке PROLOG имеется способ вычисления значения по аргументам. Это так называемые встроенные функции, которые могут быть заменены на свое значение, если их аргумент известен. Такими функциями служат, в частности, числовые арифметические операции. Заметим, что даже встроенная функция не вычисляется, пока не будет дан явный сигнал. У Вас в программе может, скажем, накопиться в качестве значения переменной выражение 1 + 1 + 1, но оно не будет равно 3.

Для организации вычисления имеется специальное отношение X is E. В этом отношении Е является таким выражением, которое после подстановки текущих значений переменных конкретизируется7) в композицию встроенных функций от константных аргументов. Эта композиция вычисляется, и переменная Х унифицируется как ее значение.

Таким образом, можно постепенно накапливать вычисления, а затем в подходящий момент их произвести. Смотрите пример.

?- assert(a(1+1)). Yes ?- assert(b(2 * 2)). Yes ?- a(X), b(Y), Z is X + Y. X = 1+1 Y = 2*2 Z = 6

Внимание!

is не является присваиванием! Для того, чтобы убедиться в этом, исполните простейшее предложение языка PROLOG:

?- X is 1, X is X + 1.

Лучше всего и естественней всего вводятся в PROLOG-программу данные, полностью соответствующие синтаксису предложений и фактов языка. Если файл данных не очень велик, то для ввода достаточно воспользоваться уже описанным нами предикатом consult.

Конечно же, имеется и более традиционная система ввода-вывода. Опишем ее базовые возможности.

open(SrcDest, Mode, Stream, Options)

Открытие файла. SrcDes является атомом, содержащим имя файла в обозначениях системы Unix. Mode может быть read, write, append или update. Два последних способа открытия используются, соответственно, для дописывания в существующий файл и для частичного переписывания его. Stream либо переменная, и тогда ей присваивается целое число, которое служит для идентификации файла, либо атом, и тогда он служит внутри программы именем файла.
Options могут быть опущены, среди них нам важна одна опция: type(binary), которая позволяет записать коды в двоичный файл. Опции образуют список.

Конечно же, имеется возможность вручную установить текущую позицию внутри файла:

seek(Stream, Offset, Method, NewLocation)

Method — это метод отсчета относительной позиции. bof отсчитывает ее с начала файла, current от нынешней точки, eof от конца. Переменная NewLocation унифицируется с новой позицией, отсчитываемой обязательно с начала.

Предикат close(Stream) комментариев не требует.

read(Stream, Term)

Переменная Term унифицируется с термом, прочитанным из потока Stream.



read_clause(Stream, Term)

Читается предложение. По умолчанию пользователя предупреждают о переменных, которые отсутствуют в голове и лишь однажды присутствуют в хвосте.

read_term(Stream, Term, Options)

Аналогично read, но позволяет установить целый ряд возможностей, регулирующих представление терма. Смотрите подробнее в документации конкретной PROLOG-системы.

writeq(Stream, Term)

Term пишется в Stream, вставляются кавычки и скобки, где нужно.

write_canonical(Stream, Term)

Term пишется в Stream таким способом, чтобы его однозначно прочитала любая PROLOG-программа, а не только Вы и Ваша программа на Вашей системе.

Есть способ читать и писать символы, а через них строки и прочее, но это настолько примитивно и уродливо, что можно дать практический совет:

Внимание!

Если Вам нужно ввести в PROLOG файл иноязычного или просто обычного формата либо вывести из него в предписанном Вам не укладывающемся в систему термов формате, напишите переходник на Рефале или на Perl.

Тем не менее вот минимальный (и практически полный) список предикатов символьного и двоичного ввода и вывода.

get_byte(Stream, Byte)

Byte рассматривается как целое число и унифицируется со следующим байтом входного потока. Конец файла читается как -1.

get_char(Stream, Char)

Аналогично, но следующий байт рассматривается как имя атома, состоящее из одного символа. Конец файла унифицируется с атомом end_of_file.


Русские буквы, пробелы и прочие нестандартные символы могут вызвать неприятности.

get(Stream, Char)

Аналогично, но пропускаются невидимые символы.

skip(Stream, Char)

Пропускает все, пока не встретится символ Char либо конец файла. Само первое вхождение Char также будет пропущено.

put(Stream, Char)

Вывод одного символа либо байта. Char унифицируется либо как целое число из диапазона [0, 255], либо как атом с именем из одного символа.

nl(+Stream)

Вывести перевод строки.

  1)

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

  2)

  Внутри языка они называются атомами.

  3)

  В части реализаций эти ограничения могут распространяться и на функции, определенные программистом.

  4)

  Мысознательно отказались от сопоставления двух видов целей в PROLOG с конъюнкцией и дизъюнкцией, поскольку их семантика принципиально отличается от семантики этих логических связок.

  5)

 

Более того, можно было бы унифицировать любые соответственные друг другу внутренние подвыражения в произвольном порядке, лишь бы объемлющие унифицировались после подчиненных. Автору неизвестно ни одно использование этого естественного и многообещающего обобщения алгоритма унификации.

В первой реализации языка PROLOG, основные особенности которой стали в дальнейшем фактическим стандартом, создатели допустили ошибку в понимании и, соответственно, в реализации алгоритма унификации, которая, к примеру, разрушает свойство частичного исполнения и может привести к излишней конкретизации, в то время как можно было бы найти более общую конкретизирующую подстановку. Эта ошибка несущественна, она не влияет на подавляющее большинство программ, но иногда она приводит к появлению бесконечных термов, и некоторые реализаторы языка PROLOG с гордостью пишут, что они умеют печатать и показывать на экране даже такие термы.

Желающие в качестве упражнения выловить ошибку самостоятельно, сравните алгоритмы унификации, описанные в книге [30] и [12].

  6)

  Здесь мы не оговорились: в данном случае предложения лучше трактовать как операторы, поскольку как утверждения их в данном случае рассматривать невозможно.

  7)

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

Содержание раздела