Prolog check if first element in lists are not equ

2019-03-02 05:51发布

I want to compare 2 lists, the first element should not be equal, the second one should be equal.

example database:

likes(josh,muse).
likes(sam,muse).
likes(josh,gnr).
likes(sam, radiohead).

so it should return true for same([josh,muse], [sam,muse]).

This is what I tried so far:

same([H1|R1], [H2|R2]):-
    H1 \= H2,
    same(R1,R2).

This returned false for every combination.

标签: list prolog
2条回答
相关推荐>>
2楼-- · 2019-03-02 06:16

The [Head|Tail] Prolog notation for lists that you're using in the definition of the same/2 predicate provides access to the list head and tail. The tail of a list is itself a (possibly empty) list. But in your case you want to access the first and the second elements, which you can do by writing [First, Second| _] (i.e. by enumerating the elements separated by a comma; here I'm using an anonymous variable for the tail as we don't need it and therefore we can ignore it).

Your predicate can be fixed by rewriting it as:

same([F1,S1|_], [F2,S2|_]):-
    F1 \== F2,
    S1 == S2.

If you know that the arguments are always lists with two elements, you can simplify the predicate to:

same([F1,S1], [F2,S2]):-
    F1 \== F2,
    S1 == S2.

Sample calls:

?- same([josh,muse], [sam,muse]).
true.

?- same([sam,muse], [sam,muse]).
false.

?- same([josh,muse], [sam,maria]).
false.

As a last note, your question is about term equality but in your solution attempt you're using term unification. These have different semantics and should not be confused.

查看更多
我命由我不由天
3楼-- · 2019-03-02 06:33

In reading your question you first gave a database of facts

likes(josh,muse).
likes(sam,muse).
likes(josh,gnr).
likes(sam, radiohead).

but then used list for the predicate

same([josh,muse], [sam,muse]).

As Paulo answered starting with list, I will answer starting with facts.

The first thing is to create a predicate that reads the facts, does some logic and returns results.

same_1(P1,P2,Item) :-
    likes(P1,Item),
    likes(P2,Item).

which gives

?- same_1(P1,P2,Item).
P1 = P2, P2 = josh,
Item = muse ;
P1 = josh,
P2 = sam,
Item = muse ;
P1 = sam,
P2 = josh,
Item = muse ;
P1 = P2, P2 = sam,
Item = muse ;
P1 = P2, P2 = josh,
Item = gnr ;
P1 = P2, P2 = sam,
Item = radiohead.

So this needs to make sure P1 is not the same as P2.

same_2(P1,P2,Item) :-
    likes(P1,Item),
    likes(P2,Item),
    P1 \= P2.

which gives

?- same_2(P1,P2,Item).
P1 = josh,
P2 = sam,
Item = muse ;
P1 = sam,
P2 = josh,
Item = muse ;
false.

Still two answers that are valid but essentially a duplicate. To remove these duplicates requires storing all of the results so that each new result can be checked against existing results and not added to the current results. Also before storing the results they need to be normalized so that no matter which way the names are ordered when initially created they are in the same order when comparing them before saving them.

Modifying the code to create normalized entries.

same_3(P1,P2,Item) :-
    likes(T1,Item),
    likes(T2,Item),
    T1 \= T2,
    normalize(T1,T2,P1,P2).

normalize(P1,P2,P1,P2) :- P1 @> P2.
normalize(P1,P2,P2,P1) :- P1 @=< P2.

which returns

?- same_3(P1,P2,Item).
P1 = sam,
P2 = josh,
Item = muse ;
P1 = sam,
P2 = josh,
Item = muse ;
false.

Notice that this result has the names in the same order.

Now to just save the results as they are generated and only add unique items to the result. This is done using setof/3.

?- setof((P1,P2,Item),(same_3(P1,P2,Item)),Bag).
Bag = [(sam, josh, muse)].
查看更多
登录 后发表回答