Важным моментом является то, что мы хотим, чтобы эта программа работала правильно, какую бы структуру мы ей ни задали. Понятно, что одна из возможностей сделать это – обеспечить отдельное утверждение для каждого функтора, какой только можно представить. Но это работа, которую мы никогда не завершим, потому что существует бесконечно много различных функторов! Написать подобную программу можно, используя встроенные предикаты для работы со структурами произвольного вида. Здесь мы опишем некоторые из них – это предикаты functor, arg и '=..'. Мы опишем также предикат name, выполняющий операции над атомами.
Предикат functor определен таким образом, что functor(T,F,N) означает, что Т –
?- functor(f(a,b,g(Z)),F,N).
Z = _23, F = f, N
?- functor(a+b,F,N).
F = +, N = 2
?- functor([a,b,c],F,N).
F =., N
?- functor(apple,F,N).
F = apple, N = 0
?- functor([a,b,c],'.',3).
нет
?- functor([a,b,c],a,Z).
нет
Прежде чем перейти к обсуждению предиката arg, следует рассмотреть второй способ использования предиката functor. В этом случае первый аргумент целевого утверждения functor (Т, F, N) неконкретизирован. В этом случае два других аргумента должны быть конкретизированы, однозначно определяя функтор и число аргументов соответственно. Целевое утверждение такого вида всегда согласуется с базой данных, и в результате значением Т становится структура с указанными функтором и числом аргументов. Таким образом, это некоторый способ
Предикат functor используется для создания структуры в основном тогда, когда нам надо получить «копию» некоторой уже существующей структуры с новыми переменными в качестве аргументов. Мы можем ввести для этого предикат копирование, использующий functor как целевое утверждение:
копирование(Старая, Новая):- functor(Cтapaя,F,N), functor(Hoвaя,F,N).
В этом определении подряд используются два целевых утверждения functor. Если целевое утверждение копирование имеет конкретизированный первый аргумент и неконкретизированный второй, то произойдет следующее. Первое целевое утверждение functor будет соответствовать первому способу использования этого предиката (так как первый аргумент этого предиката конкретизирован). Следовательно, F и N конкретизируются, получив в качестве значений функтор и число аргументов этой существующей структуры. Второе целевое утверждение functor соответствует второму способу использования этого предиката. На этот раз первый аргумент неконкретизирован, и информация, задаваемая F и N, используется для создания структуры Новая. Эта структура имеет те же функтор и число аргументов, что и Старая, но ее компонентами являются переменные. Таким образом, возможен следующий диалог:
?- копирование(sentence(np(n(john)), v(eats)),X).
X = sentence(_23,_24)
Мы используем подобную комбинацию целевых утверждений functor в определении предиката reconsult в разд. 7.13.
Предикат arg всегда должен использоваться с конкретизированными первым и вторым аргументами. Он применяется для доступа к конкретному аргументу структуры. Первый аргумент предиката arg определяет, какой аргумент структуры необходим. Второй аргумент определяет структуру, к аргументу которой необходим доступ. Пролог находит соответствующий аргумент и затем пытается сопоставить его с третьим аргументом предиката arg. Таким образом, цель arg(N,T,A) согласуется с базой данных, если N-й аргумент Т есть А. Давайте рассмотрим несколько целевых утверждений с arg.
?- аrg(2,отношение(джон,мать(джейн)),Х).
X = мать(джейн)
?- arg(1,a+(b+c),X).
X =а
?- arg(2,[a,b,c],X).
X = [b,c]
?-arg(l,a+(b+c),b).
нет