Решение с cut ... сначала это звучит довольно хлопотно.
Если предположить, что будет создан первый аргумент, решение тривиально:
once_member(X,L):-
member(X,L),!.
, но это не такПоведение, которое вы хотите, если первый аргумент не был создан.
Если мы знаем домен элементов списков (например, числа от 1 до 42), мы можем создать первый аргумент:
once_member(X,L):-
between(1,42,X),
member_(X,L).
member_(X,L):-
member(X,L),!.
но на данный момент это очень неэффективно
, я начал верить, что это невозможно сделать только с помощью разреза (при условии, что мы не используем + или list_to_set / 2
ох, подождите! <вставьте смайлик идеиздесь>
Если бы мы могли реализовать предикат (например, list_to_set / 2 swi-prolog), который бы взял список и создал список, в котором удалены все дублирующиеся элементы, мы могли бы простоиспользуйте обычный member / 2 и не получите повторяющихся результатов. Попробуйте, я думаю, что вы сможете написать это сами.
-------- Спойлеры ------------
one_member(X,L):-
list_to_set(L,S),
member(X,S).
list_to_set([],[]).
list_to_set([H|T],[H|S]):-
remove_all(H,T,TT),
list_to_set(TT,S).
%remove_all(X,L,S): S is L if we remove all instances of X
remove_all(_,[],[]).
remove_all(X,[X|T],TT):-
remove_all(X,T,TT),!.
remove_all(X,[H|T],[H|TT]):-
remove_all(X,T,TT).
Как вы видите, мы должны использовать вырез в remove_all / 3, поскольку в противном случае третье предложение может быть сопоставлено с remove_all(X,[X|_],_)
, поскольку мы не указываем, что H отличается от X. Я считаю, что решение с notтривиальный.
Кстати, решение with не может быть охарактеризовано как более декларативное, чем решение с cut;вырез, который мы использовали, обычно называется красным разрезом, поскольку он изменяет поведение программы.И есть другие проблемы;обратите внимание, что даже с разрезом remove_all(1,[1,2],[1,2])
будет успешным.
С другой стороны, неэффективно дважды проверять условие.Поэтому оптимальным было бы использование структуры if-then-else (но я предполагаю, что вам также не разрешено использовать ее; ее реализация может быть выполнена с разрезом).
С другой стороны,есть другая, более легкая реализация с not: вам нужно не только проверить, является ли X членом списка, но также и то, встречались ли вы ранее;поэтому вам понадобится аккумулятор:
------------- Спойлеры --------------------
once_member(X,L):-
once_member(X,L,[]).
once_member(X,[X|_T],A):-
\+once_member(X,A).
once_member(X,[H|T],A):-
once_member(X,T,[H|A]).