|
НАЗАД
Шаг 1: ТЦ: append([2,3],[1,4],L3).
Пр1: [2,3] не сопоставим [] Þ неуспех
Пр2: append([2|[3]],[1,4],[2|T31):¾ append([3],[1,4],T31).
где L3=[2|T31].
ТР: append([3],[1,4],T31).
Шаг 2: ТЦ: append([3],[1,4],T31).
Пр1: [3] не сопоставим []Þ неуспех
Пр2: append([3|[]],[1,4],[3|T32]):¾ append([],[1,4],T32).
где T31=[3|T32].
Шаг 3: ТЦ: append([],[1,4],T32).
Пр1: [] сопоставим []Þ успех
Получается подстановка {T32=[1,4]}.
Переход на шаг 2, и конкретизация переменной T31 значением [3|[1,4]] или [3,1,4].
Переход на шаг 1, и конкретизация переменной L3 значением [2|[3,1,4]] или [2,3,1,4].
ТР: ÿ.
Результат вычисления запроса: append([2,3],[1,4],L3). Þ успех при подстановке {L3=[2,3,1,4]}.
Хотя программа для append выглядит довольно просто, она обладает большой гибкостью и ее можно использовать многими другими способами. Например, ее можно применить как бы в обратном направлении для разбиения заданного списка на две части:
?- append ( LI, L2, [а,b,с] ).
Процедура вычисления будет следующей:
L1 = []
L2 = [а,b,с];
L1 = [а]
L2= [b,с];
L1 = [а,b]
L2 = c;
LI = [а,b,с]
L2 = [];
no (нет)
Список [a,b,c] разбивается на два списка четырьмя способами, и все они были обнаружены нашей программой при помощи механизма автоматического перебора (рис. 33).
Нашу программу можно также применить для поиска в списке комбинации элементов, отвечающей некоторому условию, задаваемому в виде шаблона или образца. Например, можно найти все месяцы, предшествующие данному, и все месяцы, следующие за ним, сформулировав такую цель:
?-append( Until, [‘май’ | After],[‘янв’,’фев’,’март’,’апр’,’май’,’июнь’,’июль’, ‘авг’,’сент’,’окт’,’ноябрь’,’дек’]).
Until = [‘янв’,’фев’,’март’,’апр’]
After = [‘июнь’,’июль’,’авг’,’сент’,’окт’,’ноябрь’,’дек’].
Далее мы сможем найти месяц, непосредственно предшествующий маю, и месяц, непосредственно следующий за ним, задав вопрос:
?- append ( _, [Month1,’май’,’Month2’|], [‘янв’, ’фев’,’март’,’апр’,’май’, ’июнь’,’июль’,‘авг’,’сент’,’окт’,’ноябрь’,’дек’]).
Month1 =апр
Month2 =июнь
Более того, мы сможем, например, удалить из некоторого списка L1 все, что следует за тремя последовательными вхождениями элемента z в L1 вместе с этими тремя z. Например, это можно сделать так:
?- LI = [a,b,z,z,c,z,z,z,d,e], append ( L2, [z,z,z |_ ], L1).
LI = [a,b,z,z,c,z,z,z,d,e]
L2 = [a,b,z,z,c]
Мы уже запрограммировали отношение принадлежности (предикат member). Однако, используя append, можно было бы определить это отношение следующим эквивалентным способом:
member1( X, L) :- append ( L1, [X | L2], L).
В этом предложении сказано: «X принадлежит L, если список L можно разбить на два списка таким образом, чтобы элемент X являлся головой второго из них. Разумеется, member1определяет то же самое отношение, что и member. Мы использовали другое имя только для того, чтобы различать таким образом две разные реализации этого отношения. Заметим, что, используя анонимную переменную, можно записать вышеприведенное предложение так:
member1( X, L) :- append (_, [X |_], L).
Интересно сравнить между собой эти две реализации отношения принадлежности. memberимеет довольно очевидный процедурный смысл:
Для проверки, является ли X элементом списка L, нужно
- сначала проверить, не совпадает ли голова списка L с X, а затем
- проверить, не принадлежит ли X хвосту списка L.
С другой стороны, member1, наоборот, имеет очевидный декларативный смысл, но его процедурный смысл не столь очевиден.
Интересным упражнением было бы следующее: выяснить, как в действительности member1 что-либо вычисляет. Некоторое представление об этом мы получим, рассмотрев запись всех шагов вычисления ответа на вопрос:
?- member1(b,[a,b,c]).
НАЗАД
|