user1816481
user1816481

Reputation: 107

How can i tell if an object is unique

i just can't get my head around this problem i'm having with prolog. Only just started, but i can't seem to find a way to find out if an object is unique. Heres my code:

/* (Student Name, Student Number)*/
Student(stuart, 11234).
Student(ross, 11235).
Student(rose, 11236).
Student(stuart, 11237).

how can i find out if a student is unique. Take for example Stuart, there's two students named Stuart, so Stuart is not unique. How could i write a procedure to tell if its another Student called Stuart.

I've tried spending so many hours on this, but i can't seem to get my head around dealing with the original Stuart rather than the other Stuart because i can't exclude the one i'm trying to find out if its unique.

Thanks for the help.

Upvotes: 0

Views: 194

Answers (3)

CapelliC
CapelliC

Reputation: 60034

With your database example this could do

unique(S) :-
    student(S, N), \+ (student(S, M), M \= N).

as it yields

?- unique(S).
S = ross ;
S = rose ;
false.

Generally, Prolog is targeted toward existence of solutions. Then predication about cardinality need some support from the 'impure' part of the language: nb_setarg it's currently our best friend when we need to efficiently tracking cardinality.

Using a metapredicate like this:

%%  count_solutions(+Goal, ?C)
%
%   adapted from call_nth/2 for http://stackoverflow.com/a/14280226/874024
%
count_solutions(Goal, C) :-
    State = count(0, _), % note the extra argument which remains a variable
    (   Goal,
        arg(1, State, C1),
        C2 is C1 + 1,
        nb_setarg(1, State, C2),
        fail
    ;   arg(1, State, C)
    ).

:- meta_predicate count_solutions(0, ?).

you could solve the problem without considering the second argument

unique(S) :-
    student(S, _), count_solutions(student(S, _), 1).

The same predicate could use aggregate_all(count, student(S,_), 1) from library(aggregate), but such library currently builds a list internally, then you could consider the answer from Peter as easier to implement.

Upvotes: 2

Peter Hude
Peter Hude

Reputation: 101

There are probably quite a few ways to solve this problem but I would do it this way:

% a student has a name and a number
student(stuart, 11234).
student(ross, 11235).
student(rose, 11236).
student(stuart, 11237).

This code says "find a list the same length as the number of students with Name" and then "make Count the same as the length of the list":

% for every student name there is an associated count of how many times 
%   that name appears
number_students(Name, Count) :-
    findall(_, student(Name, _), Students),
    length(Students, Count).

This predicate will only be true if the number_students is 1:

% a student name is unique (appears once and only once) is the 
%  number_students count is 1
unique_student(Name) :-
    number_students(Name, 1).

Testing:

12 ?- unique_student(ross).
true.

13 ?- unique_student(rose).
true.

14 ?- unique_student(bob).
false.

15 ?- unique_student(stuart).
false.

This is an easy way to solve the problem, but it isn't a great Prolog solution because you cannot say things like "give me a unique student name" and get a list of all the unique names.

Upvotes: 2

user1812457
user1812457

Reputation:

Some comments on the code you have. This is not a fact:

Student(Ross).

These are two different facts (in SWI-Prolog, at least):

student(ross).
student('Ross').

In other words, predicate names must start with small letters, and identifiers starting with a capital letters denote variables, not atoms. You can put any character string in single quotes to make it a valid atom.

Now this out of the way, it is not clear what you are aiming at. What are you going to do with your unique student? How do you know the first one is the one you are looking for, and not the second? And why not use the student number for that (at least in your example the two Stuarts seem to have different numbers)?

Upvotes: 1

Related Questions