Frank Sheng
Frank Sheng

Reputation: 205

Check the predicates are distinct

I have a SWI-Prolog code:

class(c1, 'Vehicle').
class(c2, 'Car').
class(c3, 'Motorcycle').
class(c4, 'Company').
class(c5, 'RentalStation').
class(c6, 'Person').

I want to write a rule to check that all predicates of class have the distinct name(the second parameter). How should I write this rule?

Upvotes: 1

Views: 915

Answers (2)

tas
tas

Reputation: 8140

Another possibility to do this is to use bagof/3 and maplist/2. To get all names you can use bagof/3 like so:

?- bagof(Y,X^class(X,Y),List).
List = ['Vehicle', 'Car', 'Motorcycle', 'Company', 'RentalStation', 'Person'].

To get all ids you can use bagof/3 like so:

?- bagof(X,Y^class(X,Y),List).
List = [c1, c2, c3, c4, c5, c6].

You can use maplist/2 to define a predicate that describes a list with unique elements, let's call it uniques/1, like so:

:- use_module(library(apply)).   % for maplist/2

uniques([]).
uniques([X|Xs]) :-
   maplist(dif(X),Xs),
   uniques(Xs).

You can query this predicate to see that it really describes what you want:

?- uniques([]).
true.

?- uniques([a,b,c]).
true.

?- uniques([a,b,c,a]).
false.

Building on this, you can check if the names or the ids are unique like so:

?- bagof(Y,X^class(X,Y),List), uniques(List).
List = ['Vehicle', 'Car', 'Motorcycle', 'Company', 'RentalStation', 'Person'].

?- bagof(X,Y^class(X,Y),List), uniques(List).
List = [c1, c2, c3, c4, c5, c6].

If you want to get an answer that shows you the repetitions in the list you can define a predicate list_reps/2 like so:

:- use_module(library(lists)).          % for append/3

list_reps([],[]).
list_reps([X|Xs],Ds1) :-
   x_reps_others_fromlist(X,Ds,Os,Xs),
   list_reps(Os,Ds0),
   append(Ds,Ds0,Ds1).

x_reps_others_fromlist(_X,[],[],[]).
x_reps_others_fromlist(X,[X|Ds],Os,[X|Ys]) :-
   x_reps_others_fromlist(X,Ds,Os,Ys).
x_reps_others_fromlist(X,Ds,[Y|Os],[Y|Ys]) :-
   dif(Y,X),
   x_reps_others_fromlist(X,Ds,Os,Ys).

Now let's see this predicate at work:

?- list_reps([],R).
R = [].

?- list_reps([a,b,c],R).
R = [] ;
false.

?- list_reps([a,b,c,a],R).
R = [a] ;
false.

?- list_reps([a,b,c,a,b],R).
R = [a, b] ;
false.

?- list_reps([a,b,c,a,b,a],R).
R = [a, a, b] ;
false.

As you see, elements appear as often in the second list as they are repeated. With the facts class/2 you provided there are neither repeated names nor repeated ids:

?- bagof(Y,X^class(X,Y),List), list_reps(List,R).
List = ['Vehicle', 'Car', 'Motorcycle', 'Company', 'RentalStation', 'Person'],
R = [] ;
false.

?- bagof(X,Y^class(X,Y),List), list_reps(List,R).
List = [c1, c2, c3, c4, c5, c6],
R = [] ;
false.

To see two counterexamples let's add two additional facts:

class(c1, 'Vehicle').
class(c2, 'Car').
class(c3, 'Motorcycle').
class(c4, 'Company').
class(c5, 'RentalStation').
class(c6, 'Person').
class(c6, 'Person').          % <- new
class(c7, 'Person').          % <- new

Now the above queries tell you that the name 'Person' is repeated twice and the id c6 is repeated once:

?- bagof(Y,X^class(X,Y),List), list_reps(List,R).
List = ['Vehicle', 'Car', 'Motorcycle', 'Company', 'RentalStation', 'Person', 'Person', 'Person'],
R = ['Person', 'Person'] ;
false.

?- bagof(X,Y^class(X,Y),List), list_reps(List,R).
List = [c1, c2, c3, c4, c5, c6, c6, c7],
R = [c6] ;
false.

For comparison reasons: The two queries from further above that check if the names or ids are unique both fail with the additional facts:

?- bagof(Y,X^class(X,Y),List), uniques(List).
false.

?- bagof(X,Y^class(X,Y),List), uniques(List).
false.

Upvotes: 2

code_x386
code_x386

Reputation: 798

You can first use findall/3 predicate to get list of all class names, and then simply check that list has no duplicate elements, for example:

findall(Name, class(_, Name), Names),
sort(Names, Unique),
length(Names, X),
length(Unique, X).

Here we first get all names into Names variable and then use a little "trick" to check that all elements are unique, namely, we use sort/2 predicate, which not only does the sort but also removes duplicates (unlike msort/2). After that we simply compare length of original and de-duplicated list.

Update: in case if you need to check uniqueness per specific id, you can use another invocation of findall/3 to get list of all ids, and then combine maplist/2 and the solution above to check uniqueness per each id. Combining everything together, we get:

class(c1, 'Vehicle').
class(c1, 'Vehicle').
class(c2, 'Car').
class(c3, 'Motorcycle').
class(c4, 'Company').
class(c5, 'RentalStation').
class(c6, 'Person').

all_unique :-
    findall(Name, class(_, Name), Names),
    sort(Names, Unique),
    length(Names, X),
    length(Unique, X).

all_unique_per_id :-
    findall(Id, class(Id, _), Ids),
    maplist(unique_per_id, Ids).

unique_per_id(Id) :-
    findall(Name, class(Id, Name), Names),
    sort(Names, Unique),
    length(Names, X),
    length(Unique, X).

Upvotes: 0

Related Questions