Подтвердить что ты не робот

Удалить дубликаты в списке (Prolog)

Я совершенно новичок в Prolog и пробую некоторые упражнения. Один из них:

Напишите набор предикатов (InList, OutList) который принимает на вход произвольное список и возвращает список, в котором каждый элемент списка ввода появляется только один раз.

Вот мое решение:

member(X,[X|_]).
member(X,[_|T]) :- member(X,T).

set([],[]).
set([H|T],[H|Out]) :-
    not(member(H,T)),
    set(T,Out).
set([H|T],Out) :-
    member(H,T),
    set(T,Out).

Мне не разрешено использовать какие-либо встроенные предикаты (лучше даже не использовать not/1). Проблема в том, что set/2 дает несколько одинаковых решений. Чем больше повторений в списке входных данных, тем больше решений. Что я делаю не так? Спасибо заранее.

4b9b3361

Ответ 1

Вы получаете многократные решения из-за обратного отслеживания Prolog. Технически каждое предлагаемое решение является правильным, и именно поэтому оно генерируется. Если вы хотите создать только одно решение, в какой-то момент вам придется прекратить откат. Для этого используется Prolog cut. Вы можете обнаружить, что чтение на этом поможет вам с этой проблемой.

Обновление: правильно. Предикат member() оценивается как true несколькими способами, если первая переменная находится в нескольких положениях во второй переменной.

Я использовал имя mymember() для этого предиката, чтобы не вступать в конфликт с GNU Prolog builtin member() предикат. Теперь моя база знаний выглядит следующим образом:

mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).

not(A) :- \+ call(A).

set([],[]).
set([H|T],[H|Out]) :-
    not(mymember(H,T)),
    set(T,Out).
set([H|T],Out) :-
    mymember(H,T),
    set(T,Out).

Итак, mymember(1, [1, 1, 1]). оценивается как true тремя различными способами:

| ?- mymember(1, [1, 1, 1]).

true ? a

true

true

no

Если вы хотите иметь только один ответ, вам придется использовать разрез. Изменение первого определения mymember() на следующее:

mymember(X,[X|_]) :- !.

Решает вашу проблему.

Кроме того, вы можете избежать not() вообще, если хотите, путем определения предиката notamember() самостоятельно. Выбор за вами.

Ответ 2

Вы на правильном пути... Оставайтесь чистыми ---it просто!

Используйте уточненные предикаты равенства =/3 и dif/3 в сочетании с if_/3, как это реализовано в объединении Prolog для AUBUC:

=(X, Y, R) :- X == Y,    !, R = true.
=(X, Y, R) :- ?=(X, Y),  !, R = false. % syntactically different
=(X, Y, R) :- X \= Y,    !, R = false. % semantically different
=(X, Y, R) :- R == true, !, X = Y.
=(X, X, true).
=(X, Y, false) :-
   dif(X, Y).

% dif/3 is defined like (=)/3
dif(X, Y, R) :- X == Y,    !, R = false.
dif(X, Y, R) :- ?=(X, Y),  !, R = true. % syntactically different
dif(X, Y, R) :- X \= Y,    !, R = true. % semantically different
dif(X, Y, R) :- R == true, !, X \= Y.
dif(X, Y, true) :-         % succeed first!
   dif(X, Y).
dif(X, X, false).

if_(C_1, Then_0, Else_0) :-
   call(C_1, Truth),
   functor(Truth,_,0),  % safety check
   ( Truth == true -> Then_0 ; Truth == false, Else_0 ).

На основе этих предикатов мы строим предикат членства list_item_isMember/3. Семантически эквивалентен memberd_truth/3 от @false. Мы переставляем порядок аргументов, чтобы список был первым аргументом. Это позволяет индексировать по первому аргументу, что предотвращает оставление бесполезных точек выбора, как memberd_truth/3.

list_item_isMember([],_,false).
list_item_isMember([X|Xs],E,Truth) :-
   if_(E = X, Truth = true, list_item_isMember(Xs,E,Truth)).

list_set([],[]).
list_set([X|Xs],Ys) :-
    if_(list_item_isMember(Xs,X), Ys = Ys0, Ys = [X|Ys0]),
    list_set(Xs,Ys0).

Простой запрос показывает, что все лишние ответы были исключены и что цель достигнута, не оставляя после себя никаких точек выбора:

?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [4,3,2,1].                         % succeeds deterministically

Редактировать 2015-04-23

Я был вдохновлен @Ludwig ответом set/2, который выглядит так:

set([],[]).
set([H|T],[H|T1]) :- subtract(T,[H],T2), set(T2,T1).

Встроенный предикат subtract/3 SWI-Prolog subtract/3 может быть немонотонным, что может ограничивать его использование. list_item_subtracted/3 является монотонным вариантом этого:

list_item_subtracted([],_,[]).
list_item_subtracted([A|As],E,Bs1) :-
    if_(dif(A,E), Bs1 = [A|Bs], Bs = Bs1),
    list_item_subtracted(As,E,Bs).

list_setB/2 подобен set/2, но основан на list_item_subtracted/3 ---not subtract/3:

list_setB([],[]).
list_setB([X|Xs1],[X|Ys]) :-
    list_item_subtracted(Xs1,X,Xs),
    list_setB(Xs,Ys).

Следующие запросы сравнивают list_set/2 и list_setB/2:

?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1], Xs).
Xs = [4,3,2,1].                          % succeeds deterministically
?- list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [1,2,3,4].                          % succeeds deterministically

?- list_set(Xs,[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b] 
...                                      % does not terminate universally
?- list_setB(Xs,[a,b]).
   Xs = [a,b]
;  Xs = [a,b,b]
;  Xs = [a,b,b,b]
...                                      % does not terminate universally

Ответ 3

Более простое (и, скорее всего, более быстрое) решение - использовать библиотечный предикат sort/2, который удаляет дубликаты в O (n log n). Определенно работает в прологе Yap и SWIPL

Ответ 4

Я думаю, что лучший способ сделать это:

set([], []).
set([H|T], [H|T1]) :- subtract(T, [H], T2), set(T2, T1).

Итак, например, ?- set([1,4,1,1,3,4],S) дает вам вывод:

S = [1, 4, 3]

Ответ 5

Добавление моего ответа на этот старый поток:

notmember(_,[]).
notmember(X,[H|T]):-X\=H,notmember(X,T).

set([],[]).
set([H|T],S):-set(T,S),member(H,S).
set([H|T],[H|S]):-set(T,S),not(member(H,S)).

Единственное достоинство этого решения состоит в том, что он использует только те предикаты, которые были введены точкой, где это упражнение появляется в исходный текст.

Ответ 6

Это работает без вырезания, но ему нужно больше строк и другой аргумент. Если я изменил [H2 | T2] на S в третьей строке, он даст несколько результатов. Я не понимаю, почему.

setb([],[],_).
setb([H|T],[H|T2],A) :- not(member(H,A)),setb(T,T2,[H|A]).
setb([H|T],[H2|T2],A) :- member(H,A),setb(T,[H2|T2],A).
setb([H|T],[],A) :- member(H,A),setb(T,[],A).
set(L,S) :- setb(L,S,[]).

Ответ 7

Вам просто нужно остановить обратный отсчет Prolog.

enter code here
member(X,[X|_]):- !.
member(X,[_|T]) :- member(X,T).

set([],[]).
set([H|T],[H|Out]) :-
   not(member(H,T)),
   !,
set(T,Out).
set([H|T],Out) :-
   member(H,T),
   set(T,Out).

Ответ 8

Используя вспомогательную функцию mymember для Tim, вы можете сделать это, если порядок элементов в наборе не важен:

mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).

mkset([],[]).
mkset([T|C], S) :- mymember(T,C),!, mkset(C,S).
mkset([T|C], S) :- mkset(C,Z), S=[T|Z].

Итак, например, ?- mkset([1,4,1,1,3,4],S) дает вам результат:

S = [1, 3, 4]

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

mkset2([],[], _).
mkset2([T|C], S, D) :- mkset2(C,Z,[T|D]), ((mymember(T,D), S=Z,!) ; S=[T|Z]).
mkset(L, S) :- mkset2(L,S,[]).

Это решение, с тем же вводом предыдущего примера, дает вам:

S = [1, 4, 3]

В этот раз элементы находятся в том же порядке, что и в списке ввода.