На этом шаге мы рассмотрим принадлежность к списку.
Предположим, что у вас есть список имен John, Leonard, Eric и Frank, и вы хотите, используя Пролог, проверить, имеется ли заданное имя в этом списке. Другими словами, вам нужно выяснить отношение "принадлежность" между двумя аргументами - именем и списком имен. Это выражается предикатом
member(name,namelist). % "name" принадлежит "namelist"
В программе, которая приведена ниже, первое предложение проверяет голову списка. Если голова списка совпадает с именем, которое вы ищете, то можно заключить, что Name (имя) принадлежит списку. Так как хвост списка не представляет интереса, он обозначается анонимной переменной. По первому предложению целевое утверждение
member(john,[john,leonard,eric,frank])
domains namelist=name* name=symbol predicates member(name,namelist) clauses member(Name,[Name |_]). member(Name,[_ |Tail]):- member(Name,Tail).
Если голова списка не совпадает с Name, то нужно проверить, можно ли Name найти в хвосте списка.
На обычном языке:
Name принадлежит списку, если Name есть первый элемент списка, илиВторое предложение member, выражающее отношение принадлежности, в Прологе выглядит так:
member(Name,[_ |Tail]):- member(Name,Tail).
В том виде, как он дан в предыдущей программе, предикат member работает двумя способами. Рассмотрим его предложения еще раз:
member(Name,[Name |_ ]) . member(Name,[_ |Tail]):- member(Name,Tail).
Name принадлежит списку, если голова совпадает с Name; если нет, то Name принадлежит списку, если оно принадлежит его хвосту.
Чтобы найти элемент списка, надо найти его голову; иначе надо найти элемент в хвосте.
member(2, [1, 2, 3, 4]).
member[X, [1, 2, 3, 4]).
В результате, первое целевое утверждение "просит" Пролог выяснить, верно ли утверждение, второе, - найти всех членов списка [1,2,3,4]. Предикат member одинаков в обоих случаях, но его поведение может быть рассмотрено с разных точек зрения.
Особенность Пролога состоит в том, что часто, когда вы задаете предложения для предиката с одной точки зрения, они будут выполнены с другой. Чтобы увидеть эту двойственность, создадим в следующем примере предикат для присоединения одного списка к другому. Определим предикат append с тремя аргументами:
append(List1,List2,List3)
Он объединяет List1 и List2 и создает List3. Еще раз воспользуемся рекурсией (на этот раз с процедурной точки зрения). Если List1 пустой, то результатом объединения List1 и List2 останется List2. На Прологе это будет выглядеть так:
append([],List2,List2).
Если List1 не пустой, то можно объединить List1 и List2 для формирования List3, сделав голову List1 головой List3. (В следующем утверждении переменная H используется как голова для List1 и для List3.) Хвост List3 - это L3, он состоит из объединения остатка List1 (т. е. L1) и всего List2. To есть:
append([H |L1],List2,[H |L3]):- append(L1,List2,L3).
Предикат append выполняется следующим образом: пока List1 не пустой, рекурсивное предложение передает по одному элементу в List3. Когда List1 станет пустым, первое предложение унифицирует List2 с полученным List3.
Предикат append определен в программе, приведенной ниже. Загрузите эту программу.
domains integerlist=integer* predicates append(integerlist,integerlist,integerlist) clauses append([],List,List). append([H |L1],List2,[H |L3]):- append(L1,List2,L3).
Задайте следующее целевое утверждение:
append([1,2,3],[5,6],L).
append([1,2],[3],L), append(L,L,LL).
Рассматривая append с декларативной точки зрения, вы определили отношение между тремя списками. Однако это отношение сохранится, даже если List1 и List3 известны, a List2 - нет. Оно также справедливо, если известен только List3. Например, чтобы определить, какие из двух списков можно объединить для получения известного списка, надо составить целевое утверждение такого вида:
append(L1,L2,[1,2,4]).
L1=[], L2=[1,2,4] L1=[1], L2=[2,4] L1=[1,2],L2=[4] L1=[1,2,4],L2=[] 4 решения
Можно также применить append, чтобы определить, какой список можно подсоединить к [3,4] для получения списка [1,2,3,4].
Запустите целевое утверждение
append(L1,[3,4],[1,2,3,4]).
L1=[1,2]
Предикат append определил отношение между входным набором и выходным набором таким образом, что отношение применимо в обоих направлениях. Задавая такое отношение, вы спрашиваете:
Который выход соответствует данному входу?
или
Который вход соответствует данному выходу?
Состояние аргументов при вызове предиката называется потоком параметров. Аргумент, который присваивается или назначается в момент вызова, называется входным аргументом и обозначается буквой i; а свободный аргумент - это выходной аргумент, обозначается буквой о.
У предиката append есть возможность работать с разными потоками параметров, в зависимости от того, какие исходные данные вы ему дадите. Однако не для всех предикатов имеется возможность быть вызванными с различными потоками параметров. Если предложение Пролога может быть использовано с различными потоками параметров, оно называется обратимым предложением. Когда вы записываете собственные предложения в Прологе, помните, что обратимые предложения имеют дополнительные преимущества, и их создание добавляет "мощности" предикатам.
На следующем шаге мы начнем рассматривать поиск всех решений для цели сразу.