Reputation: 75
I have these facts:
name(john),
name(mary),
name(jack),
When I "iterate" all facts I find for every name something like a rating(a number). I want that rating to be paired with the name something like 2-john or john-2, store them in a list and sort the list based on the number. How can I do that in ProLog?
name(PersonsName), findRating(PersonsName, Rating), fail.
Upvotes: 1
Views: 385
Reputation: 24976
@false gave the correct answer in the comment:
setof(V-K, ( name(K), rating(K, V) ), VKs)
Personal side joke for @false, that was more than :-
. :)
I will just expand on it here for those needing the missing parts.
First the facts need to be facts, i.e.
name(john),
is not a fact because it ends with a ,
. A fact needs to end with a period (.
).
Next facts are needed to associate a rating with the person.
rating(john,2).
rating(mary,3).
rating(jack,4).
Now for setof/3
The signature of setof/3 is setof(+Template, +Goal, -Set)
Goal
is the query that will return the needed data, in this case goal is name(K), rating(K,V)
, but since that is more than one statement it is wrapped in ()
becoming ( name(K), rating(K, V) )
Example run:
?- name(Name),rating(Name,Rating).
Name = john,
Rating = 2 ;
Name = mary,
Rating = 3 ;
Name = jack,
Rating = 4.
While that gives us the data needed it is not in a format that is useful and returns the results one by one.
Template
is the format of the data in the result, in this case V-K
, where V
for Value
is the rating and K
for Key
is the key. The result will be like 2-john
.
Here is an example of what we have so far demonstrated with a query and example run:
rating(V-K) :-
name(K),
rating(K,V).
?- rating(Ratings).
Ratings = 2-john ;
Ratings = 3-mary ;
Ratings = 4-jack.
Notice that the results are cumming back in the correct format but still not in a list.
Set
is what is collected into the result.
Since you asked for a sorted result, setof/3
is used instead of bagof/3
. As noted in setof/3
: sorts the result using sort/2 to get a sorted list of alternatives without duplicates.
To make it easier to run the query instead of typing the entire setof/3 each time, just put it into a predicate.
ratings(VKs) :-
setof(V-K,(name(K),rating(K,V)),VKs).
Complete code.
name(john).
name(mary).
name(jack).
rating(john,2).
rating(mary,3).
rating(jack,4).
ratings(VKs) :-
setof(V-K,(name(K),rating(K,V)),VKs).
Example run:
?- ratings(VKs).
VKs = [2-john, 3-mary, 4-jack].
Note: This could also be done using library(assoc): Association lists but for this problem using setof/3
is so much more concise.
Upvotes: 2