Пролог два списка пересечения-почему он продолжает проверять?
Мне нужно найти элементы, которые существуют в обоих списках S1 и S2, и мне нужно распечатать эти элементы (R).
Проблема в том, что когда я набираю bendri([a,b,c,d],[d,b,e],R), он возвращает правильный результат [b,d], но не останавливается. Если вы нажмете; символ, то он продолжает проверку снова и возвращает b, после этого - d.
Почему это происходит? Он должен только вернуться [b,d] и закончить свою работу.
bendri(S1,S2,R) :-
skaiciavimai(S1,S2,R).
skaiciavimai([],_,[]).
skaiciavimai([First|Tail], S2, [First|Rest]) :-
member(First, S2),
skaiciavimai(Tail, S2, Rest).
skaiciavimai([_|Tail], S2, Rest) :-
skaiciavimai(Tail, S2, Rest).
4 ответа:
Ваша проблема заключается в том, что третье предложение
skaiciavimai/3также успешно возвращается, даже если второе предложение успешно. Я думаю, вы хотите пропустить третье предложение, если второе предложение будет успешным.Для этого можно добавить галочку в третьем предложении:
Таким образом, третье предложение не выполняется, если глава первого списка находится вskaiciavimai([First|Tail], S2, Rest) :- \+ (member(First, S2)), skaiciavimai(Tail, S2, Rest).S2.
Простая история:
;означает, что пролог должен искать альтернативы, и посколькуbendri(A,B,C)определяется как "Cявляется списком элементов, которые встречаются как вA, так и вB" (согласно вашему определению), он напечатает все возможные результаты. Вы можете увидеть это наtraceниже (добавлено ниже, потому что след довольно длинный).Разрешение
Проблема заключается в том, что вы вводите точку решения в свой предикат
skaiciavimai/3: действительно, если первый аргумент является список по крайней мере с одним элементом (вида[_|_]), Среда пролога может выбрать как второе, так и третье предложение: после того, как она успешно попробовала предложениеskaiciavimai([First|Tail], S2, [First|Rest]), она также может стремиться выбрать следующее предложениеskaiciavimai([_|Tail], S2, Rest). Так как нет предиката, который мешает этому выбору быть успешным, он также найдет решение, опуская элемент head. Вы можете решить эту проблему, добавив дополнительное ограничение в последнем предложении:skaiciavimai([],_,[]). skaiciavimai([First|Tail], S2, [First|Rest]) :- member(First, S2), skaiciavimai(Tail, S2, Rest). skaiciavimai([First|Tail], S2, Rest) :- \+ member(First,S2), skaiciavimai(Tail, S2, Rest).The
\+означает что-то вроде логическое не (хотя нужно быть осторожным, потому что не является проблемной темой влогическом программировании ). Итак, теперь мы препятствуем прологу выбрать третье предложение, которое успешно даноFirstявляется членомS2. При использовании этого кода результатом запроса будет:?- bendri([a,b,c,d],[d,b,e],R). R = [b, d] ; false.Таким образом, мы изменили определение
skaiciavimai/3теперь оно читается примерно так:: "Cэто список всех элементов, которые встречаются вA, которые также встречаются вB." поскольку для того, чтобы опустить элемент изA(третье предложение), он не должен быть членомB.К лучшему предикату
В прологе цель состоит в том, чтобы сделать предикатразнонаправленным . Действительно, Вы хотите иметь возможность вызывать предикаты в разных направлениях.
bendri/3может быть реализовано таким образом, чтоbendri(A,B,[a,c])также возвращаетA = [a, c], B = [a, c] ;и т. д. (что и имеет место в данном случае). При проектировании предикатов необходимо учитывать многократное использование предиката.След
?- trace. true. [trace] ?- bendri([a,b,c,d],[d,b,e],R). Call: (6) bendri([a, b, c, d], [d, b, e], _G360) ? creep Call: (7) skaiciavimai([a, b, c, d], [d, b, e], _G360) ? creep Call: (8) lists:member(a, [d, b, e]) ? creep Fail: (8) lists:member(a, [d, b, e]) ? creep Redo: (7) skaiciavimai([a, b, c, d], [d, b, e], _G360) ? creep Call: (8) skaiciavimai([b, c, d], [d, b, e], _G360) ? creep Call: (9) lists:member(b, [d, b, e]) ? creep Exit: (9) lists:member(b, [d, b, e]) ? creep Call: (9) skaiciavimai([c, d], [d, b, e], _G448) ? creep Call: (10) lists:member(c, [d, b, e]) ? creep Fail: (10) lists:member(c, [d, b, e]) ? creep Redo: (9) skaiciavimai([c, d], [d, b, e], _G448) ? creep Call: (10) skaiciavimai([d], [d, b, e], _G448) ? creep Call: (11) lists:member(d, [d, b, e]) ? creep Exit: (11) lists:member(d, [d, b, e]) ? creep Call: (11) skaiciavimai([], [d, b, e], _G451) ? creep Exit: (11) skaiciavimai([], [d, b, e], []) ? creep Exit: (10) skaiciavimai([d], [d, b, e], [d]) ? creep Exit: (9) skaiciavimai([c, d], [d, b, e], [d]) ? creep Exit: (8) skaiciavimai([b, c, d], [d, b, e], [b, d]) ? creep Exit: (7) skaiciavimai([a, b, c, d], [d, b, e], [b, d]) ? creep Exit: (6) bendri([a, b, c, d], [d, b, e], [b, d]) ? creep R = [b, d] ; Redo: (11) lists:member(d, [d, b, e]) ? creep Fail: (11) lists:member(d, [d, b, e]) ? creep Redo: (10) skaiciavimai([d], [d, b, e], _G448) ? creep Call: (11) skaiciavimai([], [d, b, e], _G448) ? creep Exit: (11) skaiciavimai([], [d, b, e], []) ? creep Exit: (10) skaiciavimai([d], [d, b, e], []) ? creep Exit: (9) skaiciavimai([c, d], [d, b, e], []) ? creep Exit: (8) skaiciavimai([b, c, d], [d, b, e], [b]) ? creep Exit: (7) skaiciavimai([a, b, c, d], [d, b, e], [b]) ? creep Exit: (6) bendri([a, b, c, d], [d, b, e], [b]) ? creep R = [b] ;
Этот ответ следует за ответом @gusbro... Почему бы не сохранить логическую чистоту? Это же просто!
Просто замените третье предложение следующим:
skaiciavimai([First|Tail], S2, Rest) :- non_member(First, S2), skaiciavimai(Tail, S2, Rest).И определить
non_member/2следующим образом:non_member(X, Es) :- maplist(dif(X), Es).
Это продолжение этого логически чистого ответа, представленного ранее.
:- use_module(library(lambda)). bendri(Es, Fs, Xs) :- % ^^ % || % |+----------------+\ % || || % vv vv tfilter(Fs+\E^memberd_t(E,Fs), Es, Xs). % ^^ % || % \+------------------ Fs has global scopeПример запроса, заданного OP:
?- bendri([a,b,c,d], [d,b,e], Xs). Xs = [b,d].