Reputation: 205
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
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
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