Напомним, что спецификация любого предиката (в том числе, и встроенного) на Прологе обозначается так: p/n, где p – имя предиката, n – арность предиката – количество его аргументов. Среди аргументов встроенных предикатов могут быть входные и выходные параметры. Иногда заранее нельзя сказать, будет ли i-й аргумент входным или выходным параметром, но иногда это установлено жёстко. Поэтому для более точной спецификации встроенного (и любого другого тоже) предиката вместо арности n в записи спецификации используется выражение (z1, …, zn), где zi – один из трех знаков: i, o или ?. Первый означает, что данный аргумент – входной параметр, второй – что выходной, а третий – что неопределённый.
Прежде всего, дадим определение арифметического выражения, а затем – определение встроенного предиката is/(?, i).
Арифметическое выражение – это либо простой терм, либо составной терм – совокупность простых термов, связанных знаками арифметических операций. Простой терм – это либо число, либо переменная, либо функциональный терм. Любой терм арифметического выражения должен иметь числовое значение (переменные, входящие в терм, должны быть «означены» числами). Арифметические операции традиционные: +, –, *, /, //, … В табл. 3.1 пред-
ставлен перечень арифметических операций, взятый из справочно-
го файла системы Win Prolog 4200 (фирмы LPA Ltd).
Таблица 3.1
Term |
Function |
X + Y |
adds X to Y |
X - Y |
subtracts Y from X |
- X |
returns the negative of X |
X * Y |
multiplies X by Y |
X / Y |
divides X by Y |
X // Y |
performs integer division of X by Y, truncating the result to- |
|
wards zero |
X mod Y |
computes X modulo Y, where result has the same sign as Y |
X ^ Y |
raises X to the power of Y |
abs(X) |
computes the absolute value of X |
acos(X) |
computes the arccosine of X (degrees) |
aln(X) |
computes the natural antilogarithm of X |
alog(X) |
computes the common antilogarithm of X |
|
41 |
|
Окончание табл. 3.1 |
Term |
Function |
asin(X) |
computes the arcsine of X (degrees) |
atan(X) |
computes the arctangent of X (degrees) |
cos(X) |
computes the cosine of X (degrees) |
fp(X) |
computes the fractional part of X (this has the same sign as X) |
int(X) |
computes the nearest integer less than or equal to X (truncates |
|
towards -infinity) |
ip(X) |
computes the integer part of X (truncated towards zero; this has |
|
the same sign as X) |
ln(X) |
computes the natural logarithm of X |
log(X) |
computes the common logarithm of X |
max(X,Y) |
computes the maximum of X and Y (the nearest to +infinity) |
min(X,Y) |
computes the minimum of X and Y (the nearest to -infinity) |
rand(X) |
computes a pseudo random floating point number between zero |
|
and X |
sign(X) |
computes the sign of X (-1, 0, or 1 for negative, zero or positive |
|
respectively) |
sin(X) |
computes the sine of X (degrees) |
sqrt(X) |
computes the square root of X |
tan(X) |
computes the tangent of X (degrees) |
Предикат is/(?, i) всегда записывается в инфиксном виде (без скобок). Например:
29 is (25 mod 7) * 7 + 1, X is tan(Y) + sqrt(Z + 1).
Арифметическое выражение (входной параметр) записывается справа от имени предиката is/2. Слева от имени записывается искомое или проверяемое значение этого арифметического выражения. Переменные Y и Z в данном примере должны быть «означены» – иметь числовые значения. Очевидно, что предикат is/(?, i) по определению служит либо для проверки равенства значения арифметического выражения заданному числу, либо для присваивания переменной значения, равного значению арифметического выражения. Иное использование этого предиката недопустимо.
Сравнивать значения двух арифметических выражений А и В можно с помощью предикатов из табл. 3.2.
Следует отличать представленные в этой таблице отношения равенства и неравенства от следующих отношений между термами:
42
T1 = T2 и T1 \= T2. Эти два отношения означают унифицируемость (сопоставимость) и неунифицируемость (несопоставимость) двух термов Пролога.
Таблица 3.2
Предикат |
Смысл предиката |
|
|
А =:= В |
Значения А и В равны |
А =\= В |
Значения А и В не равны |
А > В |
Значение А больше значения В |
А < В |
Значение А меньше значения В |
А >= В |
Значение А больше значения В или равно значению В |
А =< В |
Значение А меньше значения В или равно значению В |
Есть и третий вид сравнения термов: T1 = = T2 и T1 \= = T2. В этом случае термы должны быть идентичными (одинаковыми) либо быть неидентичными (неодинаковыми).
Есть несколько предикатов, предназначенных для сравнения термов, а не для сравнения значений арифметических выражений
(табл. 3.3):
Таблица 3.3
Предикат |
Смысл предиката |
||||
|
|
|
|
||
Т1 @= Т2 |
Термы Т1 |
и Т2 |
идентичны |
||
Т1 @\= Т2 |
Термы Т1 |
и Т2 |
не идентичны |
||
Т1 |
@> Т2 |
Терм Т1 |
«больше» терма Т2 |
||
Т1 |
@< Т2 |
Терм Т1 |
«меньше» терма Т2 |
||
Т1 |
@>= Т2 |
Терм Т1 |
«больше» терма Т2 или идентичен терму Т2 |
||
Т1 |
@=< Т2 |
Терм Т1 |
«меньше» терма Т2 или идентичен терму Т2 |
||
Сравнение («больше» или «меньше») производится в лексикографическом порядке имён термов (Иван @< Иванович). Если имена термов идентичны, то по порядку, слева направо, попарно сравниваются их аргументы. Если число аргументов различно, то при прочих равных условиях более «длинный» терм «больше» бо-
лее «короткого». Например: f(g(a, b), c) @< f(g(a, c), c); ff(a, b) @> f(a, b); [a, b, c] @> [a, b].
2. Встроенные предикаты ввода и вывода
Для ввода терма из входного потока данных используется предикат read/1, а для вывода терма в выходной поток данных – предикат write/1. Для перехода на новую строку и «возврата каретки»
43
используется предикат nl/0 («new line»). Все эти предикаты безвозвратны, то есть не будут повторно вызываться при возврате.
Что понимается под «входным» и «выходным потоками данных»?
По умолчанию, это, соответственно, клавиатура и экран монитора компьютера.
Если же открывается текстовый файл для чтения, то входным потоком становятся записи этого файла: при каждом вызове предиката read/1 читается очередная запись этого файла. Этой записью должен быть синтаксически правильный терм Пролога, заканчивающийся точкой. По умолчанию, последним термом, который всётаки считывается после реальной последней записи файла, является атом end_of_file.
Если же открывается текстовый файл для записи, то выходным потоком становятся записи этого файла: при каждом вызове предиката write/1 терм записывается в этот файл. Чтобы в дальнейшем этот файл мог стать файлом для чтения, в конец формируемой записи необходимо вставить точку и перейти к новой записи, то есть выполнить две процедуры:
write(’.’), nl.
Открытие файла для чтения обеспечивает предикат see/1, а открытие файла для записи – предикат tell/1. Единственным аргументом каждого из этих предикатов является путь к данному файлу в файловой структуре компьютера. Закрытие файлов обеспечивается, соответственно, предикатами seen/0 и told/0.
При вводе терма с клавиатуры следует помнить, что последним вводимым символом обязательно должна быть точка. Но при выводе терма на экран точка появляться не будет!
Можно читать из входного потока и писать в выходной поток не терм, а любой символ. Для этого используются предикаты get0/1 и put/1. Аргументом каждого из этих двух предикатов является код вводимого или выводимого символа. Оба этих предиката безвозвратного действия.
3. Встроенные предикаты управления
Можно эффективно управлять ходом логического вывода, используя специальные встроенные предикаты, например, предикат «отсечения» («cut»). В утверждениях Пролога этот предикат изо-
44
бражается с помощью восклицательного знака: !/0. Семантика этого предиката такова.
Предикат «отсечения» выполняется всегда. Решающим является побочное действие этого предиката: он устанавливает запрет на возвраты к подцелям, находящимся левее его в записи утверждения, в котором этот предикат находится. Рассмотрим, например, следующее утверждение:
Если после выполнения «отсечения» цель cj (j = 1, …, m) не может быть выполнена (дала «отказ»), то возврат может быть осуществлён к любой из целей ci (i<j). Но возврат не может быть осуществлён ни к одной из целей bk (k = 1, …, n), а также к цели a, стоящей в левой части правила.
Главной областью применения «отсечения» является повышение эффективности работы переборных алгоритмов путем удаления программистом заведомо бесперспективных или заведомо тупиковых частей графа логического вывода.
Пример 3.1. Рассмотрим фрагмент базы данных Пролога:
собрался_в_кино :– хороший_фильм(X), посмотрю(X). посмотрю(X) :– нравится(A, X), в_программе(X, B).
Допустим, многочисленные факты базы данных, определяющие предикат хороший_фильм/1, содержат информацию, взятую из энциклопедии кино, в которую включены статьи о сотнях и даже тысячах «хороших фильмов». Допустим также, что число фактов нравится(A, X) тоже велико. В них содержатся сведения, полученные у моих друзей по телефону (в моей записной книжке очень много телефонов моих друзей). Аргумент A – имя моего друга, аргумент X – название фильма. И, наконец, предположим, что многочисленные факты в_программе(X, B) извлечены из газеты «Досуг в Москве». (Аргумент X – название кинофильма, аргумент B – название кинотеатра.)
Рассмотрим логический вывод для целевого утверждения:
?– собрался_в_кино.
После 1-го шага получим
?– хороший_фильм(X), посмотрю(X).
45