Книга: Программирование на языке Пролог для искусственного интеллекта
7.1.1. Предикаты var, nоnvar, atom, integer, atomic
7.1.1. Предикаты var, nоnvar, atom, integer, atomic
Термы бывают разных типов: переменные, целые числа, атомы и т.д. Если терм — переменная, то в некоторый момент выполнения программы он может оказаться конкретизированным или не конкретизированным. Далее, если он конкретизирован, то его значение может быть атомом, структурой и т.п. Иногда бывает полезно узнать, каков тип этого значения. Например, пусть мы хотим сложить значения двух переменных X и Y:
Z is X + Y
Перед вычислением этой цели необходимо, чтобы X и Y были конкретизированы целыми числами. Если у нас нет уверенности в том, что X и Y действительно конкретизированы целыми числами, то перед выполнением арифметического действия нужно проверить это программно.
Для этого следует воспользоваться встроенным предикатом integer
(целое). Предикат integer( X)
принимает значение истина, если X — целое или если X — переменная, имеющая целое значение. Будем говорить в этом случае, что X "обозначает" целое. Цель для сложения X и Y можно тогда "защитить" такой проверкой переменных X и Y:
..., integer( X), integer( Y), Z is X + Y, ...
Если неверно, что X и Y оба являются целыми, то система и не будет пытаться их сложить. Таким образом, цели integer
"охраняют" цель Z is X + Y
от бессмысленного вычисления.
Встроенные предикаты этого типа таковы: var
(переменная), nonvar
(непеременная), atom
(атом), integer
(целое), atomic
(атомарный). Они имеют следующий смысл:
var( X)
Эта цель успешна, если X в текущий момент — не конкретизированная переменная.
nonvar( X)
Эта цель успешна, если X — терм, отличный от переменной, или если X — уже конкретизированная переменная.
atom( X)
Эта цель истинна, если X обозначает атом.
integer( X)
Цель истинна, если X обозначает целое.
atomic( X)
Цель истинна, если X обозначает целое или атом.
Следующие примеры вопросов к пролог-системе иллюстрируют применение этих встроенных предикатов:
?- var( Z), Z = 2.
Z = 2
?- Z = 2, var( Z).
no
?- integer( Z), Z = 2.
no
?- Z = 2, integer( Z), nonvar( Z).
Z = 2
?- atom( 22).
no
?- atomic( 22).
yes
?- atom( ==>).
yes
?- atom( p( 1) ).
no
Необходимость в предикате atom
продемонстрируем на следующем примере. Пусть мы хотим подсчитать, сколько раз заданный атом встречается в некоторой списке объектов. Для этого мы определим процедуру
счетчик( А, L, N)
где А
— атом, L
— список и N — количество вхождений этого атома. В качестве первой попытки можно было бы определить счетчик
так:
счетчик( _, [], 0).
счетчик( A, [A | L], N) :- !,
счетчик( A, L, N1),
% N1 - число вхождений атома в хвост
N is N1 + 1.
счетчик( А, [ _ | L], N) :-
счетчик( A, L, N).
Теперь на нескольких примерах посмотрим, как эта процедура работает:
?- счетчик( а, [а, b, а, а], N).
N = 3
?- счетчик( a, [a, b, X, Y], Na).
Na = 3
...
?- счетчик( b, [a, b, X, Y], Nb).
Nb = 3
...
?- L=[a, b, X, Y], счетчик( а, L, Na), счетчик( b, L, Nb).
Na = 3
Nb = 1
X = a
Y = a
...
В последнем примере как X, так и Y после конкретизации получили значение а
, и поэтому Nb оказалось равным только 1, однако мы хотели не этого. Нас интересовало количество реальных появлений конкретного атома, а вовсе не число термов, сопоставимых с этим атомом. В соответствии с этим более точным определением отношения счетчик
мы должны теперь проверять, является ли голова списка атомом. Усовершенствованная программа выглядит так:
счетчик( _, [], 0).
счетчик( А, [В | L], N) :-
atom( В), А = В, !, % B равно атому А?
счетчик( A, L, N1), % Подсчет в хвосте
N is N1 + 1;
счетчик( А, L, N).
% Иначе - подсчитать только в хвосте
В следующем более сложном упражнении по программированию числовых ребусов используется предикат nonvar
.