Установить предикат предиката Prolog, используя

Я пытаюсь построить простой предикат, который получает в качестве входных данных два списка, а результаты - третий, состоящий из пересечения первых двух. Я решил сделать это с помощью логического оператора. Я уверен, что моя логика правильная, но мой предикат не работает. Любые идеи?:

element(X,[H|T]) :- 
      X=H
   ;
      element(X,T).

intersection(L1,L2,R) :-
    not((
        element(A,L1),
        not(element(A,L2))
    )),
    not((
        element(A,L1),
        not(element(A,R))
    )).

Пожалуйста, не публикуйте альтернативные методы. Мне интересно, почему этот возвращает FALSE каждый раз.

Ответы

Ответ 1

Проблема заключается в том, что not/1 просто отрицает результат вашего element/2. Это не приводит к откату element/2, чтобы найти другие экземпляры, для которых будет включено приложение not/1.

Рассмотрим следующую программу.

a(1).
a(2).

b(1).
b(2).
b(3).

И следующие запросы:

  • b(X), not(a(X)).
  • not(a(X)), b(X).

Первый дает X = 3, а второй дает false. Это потому, что первый запрос сначала создает X с 1, затем с 2, затем с 3, пока окончательно не будет not(a(X)).
Второй запрос сначала создает X с 1, a(1) успешно, поэтому not(a(1)) завершается с ошибкой. Нет никакого возврата!

Ответ 2

Ваше определение correct слишком общее. Он допускает, например, что [] является пересечением любых двух списков, которые являются слишком общими. То есть он неверно преуспевает для intersection([],[a],[a]). Ему не хватает третьего слова "для всех", в котором говорится, что все элементы, которые находятся в обоих списках, будут в результирующем списке.

Но в противном случае ваше определение будет прекрасным. Для заземления. Что является немного необычным, так это то, что пересечение является первым, а не последним аргументом. Меня очень раздражают имена переменных. Я считаю, что R означает "результат", таким образом, пересечение. И L1 и L2 - это два набора для построения пересечения.

Это немного слишком общий, хотя - как и многие предикаты Prolog (подумайте о append([], non_list, non_list). Помимо списков, ваше определение допускает также термины, которые не являются ни списками, ни неполными списками:

?- intersection(non_list1,[1,2|non_list2],[3,4|non_list3]).

Чтобы сделать его действительно полезным безопасным, используйте его так:

?- when(ground(intersection(I, A, B)), intersection(I, A, B)).

или так:

?- (  ground(intersection(I, A, B))
   -> intersection(I, A, B)
   ;  throw(error(instantiation_error, intersection(I, A, B)))
   ).

Как незначительное замечание, вместо not/1 вместо not/1 напишите (\+)/1.

Ответ 3

Отсутствие обратного отслеживания из-за отрицания, как указано @SQB, на самом деле не единственная проблема с вашим кодом. Если вы немного поиграете с наземными запросами, вы обнаружите, что не-списки и пустой список, как указано @false, также не являются единственной проблемой. Рассмотрим следующие запросы:

   ?- intersection([2,3],[1,2,3],[2,3,4]).
yes
   ?- intersection([2],[1,2,3],[2,3,4]).
yes
   ?- intersection([3],[1,2,3],[2,3,4]).
yes
   ?- intersection([],[1,2,3],[2,3,4]).
yes

Первое - это то, что обычно понимается как пересечение. Остальные три являются подсписками пересечения, включая тривиальный подсписк []. Это связано с тем, как предикат описывает, что такое пересечение: в пересечении не так, что элемент находится в первом, но не во втором списке И, что указанный элемент находится в первом, но не в третьем списке. Это описание четко соответствует трем вышеуказанным запросам, поэтому они преуспевают. Обманывая немного больше с этим описанием в виду, есть некоторые другие заслуживающие внимания наземные запросы, которые преуспевают:

   ?- intersection([2,2,3],[1,2,3],[2,3,4]).
yes

Вопрос о том, является ли наличие дубликатов в решении приемлемым или нет, на самом деле является предметом обсуждения. Списки [2,2,3] и [2,3], хотя разные, представляют один и тот же набор {2,3}. Существует этот последний ответ на вопрос о союзе Prolog, который разрабатывает такие аспекты ответов. И, конечно, подсписки упомянутого выше пересечения могут также содержать дубликаты или кратность:

   ?- intersection([2,2,2],[1,2,3],[2,3,4]).
yes

Но почему это? Для пустого списка это довольно легко увидеть. Запрос

   ?- element(A,[]).
no

выходит из строя, поэтому конъюнкция element(A,L1), not(element(A,L2)) также не выполняется для L1=[]. Поэтому отрицание, обернутое вокруг, преуспевает. То же самое верно для второго отрицания, следовательно [] может быть выведено как пересечение. Чтобы понять, почему [2] и [3] преуспеть в качестве пересечения, полезно написать свой предикат как логическую формулу с явно выраженными кванторами квантов:

L1L2RA (intersection(L1,L2,R) ← ¬ (element(A,L1) ∧ ¬ element(A,L2)) ∧ ¬ (element(A,L1) ∧ ¬ element(A,R)))

Если вы обратитесь к учебнику по логике или к логическому программированию, который также показывает код Пролога как логические формулы, вы обнаружите, что универсальные кванторы для переменных, которые не встречаются в главе правила, могут быть перемещены в тело как экзистенциальные кванторы. В этом случае для A:

L1L2R (intersection(L1,L2,R) ← ∃ A ( ¬ (element(A,L1) ∧ ¬ element(A,L2)) ∧ ¬ (element(A,L1) ∧ ¬ element(A,R))))

Итак, для всех аргументов L1,L2,R существует некоторая A, которая удовлетворяет целям. Это объясняет вывод сублистов пересечения и множественных вхождений элементов.

Однако гораздо более раздражает вопрос о том, что запрос

   ?- intersection(L1,[1,2,3],[2,3,4]).

вместо создания решений. Если вы считаете, что L1 не создается и не просматривает результаты для следующего запроса

   ?- element(A,L1).
L1 = [A|_A] ? ;
L1 = [_A,A|_B] ? ;
L1 = [_A,_B,A|_C] ? ;
...

становится ясно, что запрос

   ?- element(A,L1),not(element(A,[1,2,3])).

должен зацикливаться из-за бесконечного числа списков L1, которые содержат A, описанные первым кликом. Следовательно, соответствующая конъюнкция в вашем предикате также должна зацикливаться. Кроме того, было бы неплохо, если бы такой предикат отражал реляционную природу Prolog и работал и наоборот (переменная 2-го или 3-го аргументов). Позвольте сравнить ваш код с таким решением. (Для сравнения следующий предикат описывает подсписки пересечения так же, как и ваш код, для другого определения см. Далее ниже.)

Чтобы отразить его декларативный характер, назовите его list_list_intersection/3:

list_list_intersection(_,_,[]).
list_list_intersection(L1,L2,[A|As]) :-
   list_element_removed(L1,A,L1noA),
   list_element_removed(L2,A,L2noA),
   list_list_intersection(L1noA,L2noA,As).

list_element_removed([X|Xs],X,Xs).
list_element_removed([X|Xs],Y,[X|Ys]) :-
   dif(X,Y),
   list_element_removed(Xs,Y,Ys).

Как и ваш предикат, эта версия также использует элементы пересечения для описания отношения. Следовательно, он создает одни и те же подсписки (включая []):

   ?- list_list_intersection([1,2,3],[2,3,4],I).
I = [] ? ;
I = [2] ? ;
I = [2,3] ? ;
I = [3] ? ;
I = [3,2] ? ;
no

но без циклов. Тем не менее, множественные вхождения больше не производятся, поскольку уже сопоставленные элементы удаляются с помощью list_element_removed/3. Но несколько вхождений в обоих первых списках согласованы правильно:

   ?- list_list_intersection([1,2,2,3],[2,2,3,4],[2,2,3]).
yes

Этот предикат также работает в других направлениях:

   ?- list_list_intersection([1,2,3],L,[2,3]).
L = [2,3|_A] ? ;
L = [2,_A,3|_B],
dif(_A,3) ? ;
L = [2,_A,_B,3|_C],
dif(_A,3),
dif(_B,3) ? ;
...

   ?- list_list_intersection(L,[2,3,4],[2,3]).
L = [2,3|_A] ? ;
L = [2,_A,3|_B],
dif(_A,3) ? ;
L = [2,_A,_B,3|_C],
dif(_A,3),
dif(_B,3) ? ;
...

Итак, эта версия соответствует вашему коду без дубликатов. Обратите внимание, как элемент A пересечения явно появляется в начале правила, где все элементы пересечения проходят рекурсивно. По моему мнению, это то, чего вы пытались достичь, используя неявные универсальные кванторы перед правилами Prolog.

Чтобы вернуться к точке в начале моего ответа, это не то, что обычно понимается как пересечение. Среди всех результатов list_list_intersection/3 для аргументов [1,2,3] и [2,3,4] только [2,3] - это пересечение. Здесь возникает другая проблема с вашим кодом: если вы используете элементы пересечения для описания отношения, как вы убедитесь, что вы покрываете все пересекающиеся элементы? В конце концов, все элементы [2] встречаются в [1,2,3] и [2,3,4]. Очевидная идея состояла бы в том, чтобы пройти через элементы одного из других списков и описать те, которые происходят в обоих, также находясь на пересечении. Вот вариант с if_/3 и memberd_t/3:

list_list_intersection([],_L2,[]).
list_list_intersection([X|Xs],L2,I) :-
   if_(memberd_t(X,L2),
       (I=[X|Is],list_element_removed(L2,X,L2noX)),
       (I=Is,L2noX=L2)),
   list_list_intersection(Xs,L2noX,Is).

Обратите внимание, что также можно пройти через аргументы второго списка вместо первого. Предикат memberd_t/3 является reified вариантом вашего предикатного элемента /2, а list_element_removed/3 снова используется в описании, чтобы избежать дублирования в решении. Теперь решение уникально

   ?- list_list_intersection([1,2,3],[2,3,4],L).
L = [2,3] ? ;
no

и "проблемные запросы" сверху выходят из строя, как ожидалось:

   ?- list_list_intersection([1,2,3],[2,3,4],[]).
no
   ?- list_list_intersection([1,2,3],[2,3,4],[2]).
no
   ?- list_list_intersection([1,2,3],[2,3,4],[3]).
no
   ?- list_list_intersection([1,2,3],[2,3,4],[2,2,3]).
no
   ?- list_list_intersection([1,2,3],[2,3,4],[2,2,2]).
no

И, конечно, вы также можете использовать предикат в других направлениях:

   ?- list_list_intersection([1,2,3],L,[2,3]).
L = [2,3] ? ;
L = [3,2] ? ;
L = [2,3,_A],
dif(_A,1) ? ;
...

   ?- list_list_intersection(L,[2,3,4],[2,3]).
L = [2,3] ? ;
L = [2,3,_A],
dif(4,_A) ? ;
...