Use the slot in Prolog to define the once_member / 2 function

Disclaimer: This is an informal and invaluable coursework that I do in my time. I tried this myself, failed, and now I am looking for some recommendations.

I am trying to implement a version of member / 2 function that will only return items for a list once.

For example:

| ?- member(X, [1,2,3,1]). X = 1 ? ; X = 2 ? ; X = 3 ? ; X = 1 ? ; 

I would like him to only print each number at most once.

 | ?- once_member(X, [1,2,3,1]). X = 1 ? ; X = 2 ? ; X = 3 ? ; no 

We were told to do this using the '!' but I looked through the notes for my cutting course and more online, but still I can’t do it with a click in my head!

So far I have managed to get:

 once_member(E, [E | L]) :- !. once_member(E, [_, L]) :- once_member(E, L). 

Which returns 1 and then nothing else, I feel that my cut is in the wrong place and prevents a return to every possible match, but I'm really not sure where to go next.

I looked through my comments on the course, as well as at: http://www.cs.ubbcluj.ro/~csatol/log_funk/prolog/slides/5-cuts.pdf and Programming in Prolog (Google Books)

A guide to the logical application of the cut would be most useful, but an answer could help me figure this out.

We were also told to make another method that uses the negation of "\ +" by mistake, but hopefully it could be easier if cut for me? "// p>

+8
prolog prolog-cut
source share
4 answers

Here we use an approach that uses a cut in the definition of once / 2 along with the classic member / 2 predicate:

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

Applies to the above example:

 ?- once_member(X,[1,2,3,1]). X = 2 ; X = 3 ; X = 1 ; no 

Note. . Despite the odd definition of the three sentences, once_member / 2 is the last call / tail recursion method, suitable because of the placement of the section before its first independent call.

-one
source share

Remove unnecessary answers and stay clean!

Define memberd/2 based on if_/3 and (=)/3 :

 memberd (X, [E | Es]): -
    if_ (X = E, true, memberd (X, Es)).

In particular, with meta-predicates, a different order of arguments can sometimes come in handy:

 list_memberd(Es, X) :- memberd(X, Es). 

Request example:

 ?- memberd(X, [1,2,3,1]). X = 1 ; X = 2 ; X = 3 ; false. 
+2
source share

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]). 
+1
source share
 once_member(X, Xs) :- sort(Xs, Ys), member(X, Ys). 

As with all other published solutions, this has some anomalies.

 ?- X = 1, once_member(X, [A,B]). X = A, A = 1 ; X = B, B = 1. ?- X = 1, once_member(X, [A,A]). X = A, A = 1. 
+1
source share

All Articles