Предикаты для классификации и обработки термов
В качестве аргументов предикатам можно задавать термы любого типа: числа, атомы, переменные, структуры, и при определении предиката возникает необходимость указывать, что использование некоторого утверждения ограничено определенными типами аргументов. Например, первый и второй аргументы предиката
plus(X,Y,Z):-Z is X+Y.
должны быть целыми числами, и только Z может быть неконкретизирован. Если мы хотим, чтобы предикат plus/3 находил значение любого аргумента по двум другим заданным аргументам, нам потребуются встроенные предикаты для определения типа термов.
Предикат var(X) проверяет, является ли X неконкретизированной переменной.
?-var(X).
yes
?-var(23).
no
?-X=23,var(X).
no
Предикат nonvar противоположен по значению предикату var. Цель nonvar(X) успешна, если X не является неконкретизированной переменной.
Предикат atom(X) проверяет, является ли X атомом.
?-atom(the_atom).
yes
?-atom(23).
no
?-atom('23').
yes
?-atom(f(1)).
no
?-atom(X).
no
?-atom(=>).
yes
Предикат integer(X) проверяет, является ли X целым числом. Аналогичный предикат float(X) проверяет, является ли X вещественным числом. Цель number(X) успешна, если X является целым или вещественным числом, а цель atomic(X) успешна, если X является числом или атомом. Предикат compound/1 позволяет проверить, является ли аргумент структурой.
Определение предикат сложения, допускающего различные варианты использования, выглядит так:
plus(X,Y,Z):-integer(X),integer(Y),Z is X+Y.
plus(X,Y,Z):-integer(X),integer(Z),Y is Z-X.
plus(X,Y,Z):-integer(Y),integer(Z),X is Z-Y.
Аналогично можно определить предикат
length, который позволяет находить длину списка или строить список заданной длины
:
length([],0).
length([_|T],N):-var(N), length(L,K), N is K+1.
length([_|T],N):-integer(N), N>0, K is N-1, length(L,K).
Предикат functor/3 используется для распознавания и создания структур. Цель functor(T,F,N) успешна, если T – это структура с функтором F, имеющая N компонентов. Этот предикат рассматривает атом как структуру с нулевым числом компонентов.
?-functor(f(a,b,g(c,d)),F,N).
F=f, N=3
?-functor(1+2*x,F,N).
F=+, N=2
?-functor([a,b,c],F,N).
F=., N=2
?-functor(apple,F,N).
F=apple, N=0
?-functor(T,g,3). % создание структуры
T=g(_,_,_)
Предикат arg/3 обеспечивает доступ к компонентам структуры. Цель arg(N,T,A) успешна, если A является N-м компонентом структуры T. Аргумент N должен быть целым числом, а T – структурой. Нумерация компонентов идет слева направо и начинается с 1.
?-arg(1,2+3,X).
X=2
?-arg(2,f(a,X,c),b).
X=b
?-arg(2,[a,b,c],X).
X=[b,c]
Предикат T=..L обеспечивает альтернативный способ обработки структур, полезный в том случае, если требуется получить сразу все компоненты структуры или создать структуру из функтора и списка компонентов структуры. Цель T=..L успешна, если L есть список, состоящий из функтора структуры T, за которым следуют ее компоненты.
?-f(a,b)=..X. % разбор структуры
X=[f,a,b]
?-1+2*b=..X.
X=[+,1,2*b]
?-[f,a,b]=..X.
X=[.,f,[a,b]]
?-X=..[f,a,b]. % создание структуры
X=f(a,b)
Предикат atom_codes/2 используется для преобразования атомов в список кодов букв и обратно. Цель atom_codes(X,Y) успешна, если X – атом, а Y – список ASCII-кодов символов атома X. Для аналогичного преобразования чисел применяется предикат number_codes/2.
?-atom_codes(apple,"apple").
yes
?-atom_codes(X,[97,98,99]).
X=abc
?-number_codes(12,L).
L=[49,50]
С помощью предиката atom_codes. можно выполнять различные преобразования атомов. Например, следующая программа возвращает множественное число английских слов.
мнч(mouse,mice). % исключения
мнч(sheep,sheep).
. . .
мнч(X,Y):- atom_codes(X,Xs), append(Xo,"y",Xs),
append(_,[Last],Xo), \+ member(Last,"aeiou"),
append(Xo,"ies",Ys),atom_codes(Y,Ys). % оканчивающиеся на y
мнч(X,Y):- atom_codes(X,Xs), append(_,[Last],Xs),
member(Last,"shxo"),
append(Xs,"es",Ys),atom_codes(Y,Ys). % оканчивающиеся на s,h,x,o
мнч(X,Y):-atom_codes(X,Xs),append(Xs,"s",Ys),atom_codes(Y,Ys). % общее правило
Упражнения
1. Определите предикат args(Term,List), возвращающий список аргументов структуры Term, не используя предикат =../2.
2. Определите предикат анаграмма(A1,A2), проверяющий, что атомы A1 и A2 являются анаграммами (одно слово можно получить из другого перестановкой букв).
?-анаграмма(старорежимность,нерасторжимость).
yes
3. Определите предикат argrpl(Term,N,Arg,NTerm), заменяющий N-й компонент в структуре Term на новый компонент Arg и помещающий результат в NTerm.
?-argrpl(f(a,b,c),2,z,R).
R = f(a,z,c)
4. Определите предикат make(Op,L1,L2,R), создающий список R с помощью выполнения арифметической операции Op над соответствующими элементами двух числовых списков L1 и L2 с равной длиной.
?-make(+,[1,2,3],[7,1,6],R).
R=[8,3,9]
5. Определите предикат subterm(S,T), проверяющий, что терм S является подтермом терма T.
?-subterm(t(a), f(g(b,t(a),d),a)).
yes
6. Определите предикат входит(A1,A2), проверяющий, что все буквы атома A1 входят в набор букв атома A2. Найдите те слова из словаря, заданного отношением слово/1, которые входят в заданное слово.
слово(ветер).
слово(автор).
слово(вата).
слово(ротор).
?-слово(X),входит(X,авиатор).
X=автор;
X=вата;
no
7. Определите предикат apply(Op,L,R), применяющий арифметическую операцию Op последовательно ко всем элементам списка L, например, apply(+,[7,2,5],R) возвращает в R сумму элементов, а apply(*,[7,2,5],R) – произведение.
8. Определите предикат конкрет(T) так, чтобы он был истинным, когда в терме T нет ни одной неконкретизированной переменной.