(SWI) Prolog: subgoal order

In Prolog, I have two, slightly different implementations of the predicate, unique_element / 2. The predicate succeeds if the element X and the list L are given, the element X appears only once in the list. Below are the implementations and results:

Implementation 1:

%%% unique_element/2 unique_element(Elem, [Elem|T]) :- not(member(Elem, T)). unique_element(Elem, [H|T]) :- member(Elem, T), H\==Elem, unique_element(Elem, T), !. 

Results:

 ?- unique_element(X, [a, a, b, c, c, b]). false. ?- unique_element(X, [a, b, c, c, b, d]). X = a ; X = d. 

Implementation 2:

 %%% unique_element/2 unique_element(Elem, [Elem|T]) :- not(member(Elem, T)). unique_element(Elem, [H|T]) :- H\==Elem, member(Elem, T), unique_element(Elem, T), !. 

If you did not notice at first glance: "H \ == Elem" and "member (Elem, T)" are inverted on the 2nd implementation, rule 2.

Results:

 ?- unique_element(X, [a, a, b, c, c, b]). X = a. ?- unique_element(X, [a, b, c, c, b, d]). X = a ; X = d. 

Question: How does order in this case affect the result? I understand that the rule order / facts / etc. However, two specific rules that are inverted do not seem to be “connected” or affect each other in any way (for example, the predicate “cut” in the wrong place / order).

Note. We are talking about SWI-Prolog here.

Note 2: I know maybe different and better implementations. My question here is about how to change subgoals.

+5
source share
4 answers

TL DR : read the documentation and find out why:

 ?- X = a, X \== a. false. ?- X \== a, X = a. X = a. 

I wonder why you stopped so close to understanding this :-)

There are too many ways to compare things in Prolog. At least you have a union that can sometimes be compared, and sometimes more; what you have equality, and its negation, that which you use. So what does he do:

 ?- a \== b. % two different ground terms true. ?- a \== a. % the same ground term false. 

Now this is getting interesting:

 ?- X \== a. % a free variable and a ground term true. ?- X \== X. % the same free variable false. ?- X \== Y. % two different free variables true. 

I would suggest that you do the following: figure out how member/2 does its thing (does it use unification? Equivalence? Something else?), And then replaces everything that member/2 uses in all the examples above, and sees, the results may be different.

And since you are trying to make sure everything is different, try dif/2 . How in:

 ?- dif(a, b). 

or

 ?- dif(X, X). 

or

 ?- dif(X, a). 

etc.

See also this question and answers : I think the answers are relevant to your question.

Hope this helps.

+6
source

H\==Elem tests syntactic inequality at the point in time when the target is executed. But later joining can make the variables the same:

 ?- H\==Elem, H = Elem. H = Elem. ?- H\==Elem, H = Elem, H\==Elem. false. 

So, here we check whether they are (syntactically) different, and then they are all unified and, therefore, no longer different from each other. So this is just a temporary test.

The goal of member(Elem, T) , on the other hand, is true if Elem is actually an element of T Consider:

  ?- member(Elem, [X]). Elem = X. 

What can be read as

(When) does that Elem is an element of the list [X] ?

and the answer

It is performed under certain circumstances, namely, with Elem = X

If now you mix these different goals in your programs, you get odd results that can only be explained by checking your program in detail.

As a newbie, it is best to stick to only the clean parts of Prolog. In your case:

  • use dif/2 instead of \==

  • do not use abbreviations - in your case this limits the number of answers to two. Like in unique_element(X, [a,b,c])

  • do not use not/1 and (\+)/1 . This leads to even greater inaccuracy. Consider unique_element(a,[a,X]),X=b. that failed abnormally until X=b,unique_element(a,[a,X]) correctly.


Here is a direct cleaned version of your program. There is still room for improvement!

 non_member(_X, []). non_member(X, [E|Es]) :- dif(X, E), non_member(X, Es). unique_element(Elem, [Elem|T]) :- non_member(Elem, T). unique_element(Elem, [H|T]) :- dif(H,Elem), % member(Elem, T), % makes unique_element(a,[b,a,a|Xs]) loop unique_element(Elem, T). ?- unique_element(a,[a,X]). dif(X, a) ; false. % superfluous ?- unique_element(X,[E1,E2,E3]). X = E1, dif(E1, E3), dif(E1, E2) ; X = E2, dif(E2, E3), dif(E1, E2) ; X = E3, dif(E2, E3), dif(E1, E3) ; false. 

Notice how the last request is read?

When is X unique element of (any) list [E1,E2,E3] ?

The answer is thrice. Examination of one element after another:

X E1 , but only if it is different from E2 and E3

and etc.

+7
source

Can you define unique_element as tcount Prolog - repeat the count in the list

unique_element(X, List):- tcount(=(X),List,1).

+5
source

Here is another possibility: define unique_element / 2 with if_ / 3 and maplist / 2:

 :- use_module(library(apply)). unique_element(Y,[X|Xs]) :- if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)). 

Unlike @ user27815 a very elegant solution (+ s (0)) this version is not built on clpfd (using tcount / 3 ). Examples of queries specified by the OP work as expected:

  ?- unique_element(a,[a, a, b, c, c, b]). no ?- unique_element(X,[a, b, c, c, b, d]). X = a ? ; X = d ? ; no 

The example provided by @false now succeeds without leaving an extra point of choice:

  ?- unique_element(a,[a,X]). dif(a,X) 

Another more general query yields the same results:

  ?- unique_element(X,[E1,E2,E3]). E1 = X, dif(X,E3), dif(X,E2) ? ; E2 = X, dif(X,E3), dif(X,E1) ? ; E3 = X, dif(X,E2), dif(X,E1) ? ; no 
+4
source

Source: https://habr.com/ru/post/1267039/


All Articles