Пролог два списка пересечения-почему он продолжает проверять?
Мне нужно найти элементы, которые существуют в обоих списках 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].