Программирование на языке пролог
Шрифт:
присоединить([а,b,с], [3,2,1], [а,b,с,3,2,1]).
Предикат присоединитьнаиболее часто используется для создания нового списка в результате конкатенации двух других списков, как в следующем примере:
?- присоединить ([alpha,beta],[gamma,delta],X).
X=[alpha, beta, gamma, delta]
Но он может также использоваться и другим способом;
?- присоединить(Х,[b,c,d],[a,b,c,d]). Х=[а]
Предикат присоединитьимеет
присоединить([],L,L).
присоединить([Х|L1],L2,[Х|L3]):- присоединить (L1,L2,L3).
Выход на граничное условие происходит, когда первый аргумент является пустым списком. Любой список, присоединенный к пустому списку, дает тот же самый список. Во всех других случаях будет выполняться второе правило, смысл которого можно описать словами следующим образом:
1. Первый элемент первого списка (X)всегда будет и первым элементом третьего списка.
2. Хвост третьего аргумента (L3)всегда будет представлять результат присоединения второго аргумента (L2)к хвосту первого списка (L1).
3. Для присоединения одного списка к другому, о чем шла речь в пункте 2, необходимо использовать предикат присоединить.
4. Так как при каждом обращении к правилу удаляется голова списка, являющегося первым аргументом, то постепенно этот список будет исчерпан и станет пустым, так что произойдет выход на граничное условие.
В дальнейших примерах будут встречаться ссылки на предикат присоединитьс необходимыми дополнительными пояснениями. В последующих главах мы обсудим различные свойства и применения этого предиката. Но сначала давайте применим его в другом простом примере рекурсии.
Предположим, что мы работаем на заводе, выпускающем велосипеды, и нам необходимо хранить спецификацию деталей велосипеда. Для того чтобы собрать велосипед, надо знать, какие детали заказать поставщикам. Каждая деталь велосипеда может состоять из более мелких элементов – поддеталей, например каждое колесо имеет спицы, обод и ступицу. Более того, ступица может состоять из оси и шестеренок. Давайте рассмотрим базу данных, организованную в виде дерева, которая позволит нам делать запросы о деталях, необходимых для изготовления некоторой части велосипеда. В одной из следующих глав предложенная здесь программа будет улучшена, с тем чтобы позволить вычислять, сколько экземпляров каждой детали нам потребуется.
Имеются два типа объектов, которые используются для изготовления велосипеда. Это узлыи детали.Каждый узел состоит из некоторого числа деталей, подобно тому как колесо состоит из спиц, обода и ступицы. Детали не имеют еще более мелких частей – они просто соединяются друг с другом, образуя узлы.
Можно представить детали как факты следующим образом:
деталь(обод). деталь(спица).
деталь(задняя_рама). деталь(руль).
деталь(шестерни). деталь(болт).
деталь(гайка). деталь(вилка).
Естественно,
узел(велосипед, [колесо, колесо, рама]).
Ниже представлена база данных узлов, необходимых для нашего упрощенного велосипеда:
узел(велосипед, [колесо,колесо,рама]).
узел(колесо, [спица,обод,ступица]).
узел(рама, [задняя_рама, передняя_рама]).
узел(передняя_рама, [вилка,руль]).
узел(ступица, [шестерни,ось]).
узел(ось, [болт,гайка]).
Заметим, что это частное множество утверждений неполностью описывает велосипед. Мы не делаем различия между передней и задней ступицами – обе имеют шестерни! Цепь и педали отсутствуют, и негде сидеть велосипедисту. Не указано также, как соединять детали друг с другом. Это просто перечисление некоторого числа требуемых деталей.
Теперь мы готовы написать программу, которая для заданной части перечислит все детали, необходимые для ее сборки. Если часть, которую мы хотим собрать, является деталью, то для нее ничего больше не требуется. Однако если мы хотим собрать некоторый узел, то необходимо применить этот процесс к каждой составной части узла. Определим предикат часть (X, Y),где X– имя части, a Y– список деталей, необходимых для ее сборки. В первой версии программы мы не будем рассматривать вопрос о количестве деталей каждого типа, необходимых для сборки. Более полная программа будет представлена в гл. 7.
Выход на граничное условие происходит, когда Xявляется деталью. В этом случае Xпросто возвращается в качестве элементарного списка:
часть(Х, [X]):- деталь(Х).
Следующее условие связано со случаем, когда Xявляется узлом. Здесь необходимо определить, имеется ли в базе данных соответствующий факт узел, и если такой имеется, то применить предикат частьк каждому элементу списка подчастей. Для выполнения второй из указанных задач используется предикат, названный список_частей.
часть(Х,Р):- узел(Х,Подчасти),
список_чаcтей(Подчасти, Р).
Предикат список_частейберет список частей (из второго аргумента факта узел, представленного выше) и применяет предикат частьк каждой части в списке. После вызова самого себя, необходимого для обработки хвоста списка, предикат список_частейдолжен склеить полученные списки вместе, используя предикат присоединить:
список_частей([Р|Хвост], Полный_список):- часть(Р,Части_головы)