A solution with a cut ... at first it sounds pretty troublesome.
Assuming the first argument is created, the solution is trivial:
once_member(X,L):- member(X,L),!.
but this will not have the behavior you want if the first argument is not created.
If we know the domain of the list items (for example, numbers from 1 to 42), we could create the first argument:
once_member(X,L):- between(1,42,X), member_(X,L). member_(X,L):- member(X,L),!.
but it is inefficient.
at this moment I began to believe that this cannot be done only with a cut (provided that we do not use + or list_to_set / 2
oh wait! <insert the emoticon idea here>
If we could implement a predicate (for example, list_to_set / 2 swi-prolog) that would take a list and create a list in which all duplicate elements are deleted, we could just use the regular member / 2 and not get duplicate results. Try it, I think you can write it yourself.
-------- Spoilers ------------
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).
As you can see, we must use the remove_all / 3 cut, because otherwise the third sentence could be matched by remove_all(X,[X|_],_) , since we will not indicate that H is different from X. I believe that the solution c is not trivial.
Btw, solution c cannot be characterized as more declarative than a cut decision; the cut that we used is usually called red, as it changes the behavior of the program. And there are other problems; note that even with the cut, remove_all(1,[1,2],[1,2]) will succeed.
On the other hand, it is inefficient to double check the state. Therefore, it would be optimal to use the if-then-else structure (but I assume that you are also not allowed to use it, its implementation can be done using a cut).
On the other hand, there is another, simpler implementation: no, you should not only check if X is a member of the list, but if you have encountered it before; so you need a battery:
------------- Spoilers --------------------
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]).