Prolog setof/3 not returning all elements - prolog

I'm making a list of all the breed of dog sizes in my database, e.g.
breed(beagle,medium, hunting).
breed(bassets,medium, hunting).
breed(labrador,large, guideDogs).
breed(germanShepards,large, guardDogs).
breed(boxer,unknown,unknown).
breed(dalmation,unknown,unknown).
breed(ridgeback,unknown,unknown).
so I get the sizes from the database, but want to just have the unique sizes so that I can count them later. If I use
sizes(List) :- findall(Size, breed(_,Size,_), List).
I get duplicates e.g.
[medium, medium, large, unknown, unknown, unknown]
setof/3 is meant to make the set unique, so i tried
sizes(List) :- setof(Size, breed(_,Size,_), List).
but it now only return the first entry
[medium]
Any ideas why??

Related

Prolog, strange answer of setof

I'm using the online compiler https://swish.swi-prolog.org/
Given the next facts:
frontier(spain,france).
frontier(spain,portugal).
frontier(portugal,spain).
frontier(france,spain).
frontier(france,italy).
frontier(france,germany).
frontier(france,belgium).
frontier(france,swiztland).
frontier(belgium,netherlands).
frontier(belgium,france).
frontier(belgium,germany).
frontier(netherlands,germany).
frontier(netherlands,belgium).
frontier(germany,netherlands).
frontier(germany,belgium).
frontier(germany,france).
frontier(germany,austria).
frontier(germany,swiztland).
frontier(austria,germany).
frontier(austria,swiztland).
frontier(austria,italy).
frontier(swiztland,austria).
frontier(swiztland,france).
frontier(swiztland,germany).
frontier(swiztland,italy).
frontier(italy,france).
frontier(italy,swiztland).
frontier(italy,austria).
I would like to obtain all of the countries but without obtain repeated ones.
Thus, I use a setof predicate, which avoids the repeated, like this:
setof(Country, (frontier(Country,_)), Countries).
The problem is that, when I executed the query, I obtained some iterations:
[germany, italy, swiztland]
[france, germany, netherlands]
[belgium, germany, italy, spain, swiztland],
[austria, belgium, france, netherlands, swiztland]
[austria, france, swiztland]
[belgium, germany]
[spain]
[france, portugal]
[austria, france, germany, italy]
I don't understand why, I was expected that the list Countries return me the list of all the countries without repeated ones and sorted, that's why I use the anonymus variable in the second argument of the predicate frontier, because I don't care about the second argument, only I want the first argument without repeated ones.
Any help?

Checking if all elements of list are same data type (atom I guess). Prolog

I am a total beginner in Prolog. I have some custom types: bird, animal and fish. I want to pass a list to a function like so areSameType([owl, eagle, chicken]). and get a result if the whole list is type bird or animal or fish. For example:
areSameType([owl,giraffe,shark]). > false
areSameType([owl,eagle,chicken]). > true
areSameType([cat,mouse,giraffe]). > true
The data I have inserted is:
bird(owl).
bird(eagle).
bird(chicken).
animal(cat).
animal(mouse).
animal(giraffe).
fish(shark).
fish(magikarp).
fish(gyarados).
I have tried with this function:
isSameType(X,Y):- bird(X),bird(Y);animal(X),animal(Y);fish(X),fish(Y).
areSameType([H1,H2|T]):- isSameType(H1,H2), areSameType([H2,T]).
But the problem is I don't have a criteria to check if H2 is the last element of the list or maybe I got it all wrong with this logic.
There are two problems here with your areSameType/1 predicate:
there is no stop condition: in case the list is empty, or contains exactly one element, than all the items in the list have the same type; and
you need to recurse with [H2|T] (notice the pipe |, instead of the comma ,).
So we can fix this with:
areSameType([]).
areSameType([_]).
areSameType([H1,H2|T]):-
isSameType(H1,H2),
areSameType([H2|T]).
We can however use maplist/2 [swi-doc] here, and write this predicate without recursion (well no recursion in the areSameType/1 predicate itself), like:
areSameType([]).
areSameType([H1|T]) :-
maplist(isSameType(H1), T).

Prolog, print employees with same names

This is my first time using Prolog.
I have employees:
employee(eID,firstname,lastname,month,year).
I have units:
unit(uID,type,eId).
I want to make a predicate
double_name(X).
that prints the last names of the employees with the same first name in the unit X.
I am doing something like this :
double_name(X) :-
unit(X,_,_eID),
employee(_eID,_firstname,_,_,_),
_name = _firstname,
employee(_,_name,_lastname,_,_),
write(_lastname).
But it prints all the employees in the unit.
How can i print only the employees with the same name ?
unit(unit_01,type,1).
unit(unit_01,type,2).
unit(unit_01,type,3).
employee(1,mary,smith,6,1992).
employee(2,fred,jones,1,1990).
employee(3,mary,cobbler,2,1995).
double_name(Unit) :-
unit(Unit,_,Eid_1),
employee(Eid_1,Firstname,Lastname_1,_,_),
unit(Unit,_,Eid_2),
Eid_1 \= Eid_2,
employee(Eid_2,Firstname,Lastname_2,_,_),
write(Firstname),write(","),write(Lastname_1),nl,
write(Firstname),write(","),write(Lastname_2).
Variables in Prolog typically start with an upper case letter, but starting them with and underscore is allowed, but not typical.
In double_name/2 the predicates like
unit(Unit,_,Eid_1)
employee(Eid_1,Firstname,Lastname_1,_,_)
are used to load the values from the facts into variables while pattern matching (via unification) that the bound variables match with the fact.
To ensure that a person is not compared with themselves.
Eid_1 \= Eid_2
and to make sure that two people have the same first name the same variable is used: Firstname.
The write/1 and nl/0 predicates just write the result to the screen.
Example:
?- double_name(unit_01).
mary,smith
mary,cobbler
true ;
mary,cobbler
mary,smith
true ;
false.
Notice that the correct answer is duplicated. This can be resolved.
See: Prolog check if first element in lists are not equal and second item in list is equal
and look at the use of normalize/4 and setof/3 in my answer
which I leave as an exercise for you.

Sort a parameter value in ascending orders in Prolog

I have a list of facts that have those parameters: Name, Longitude, Latitude. I want to write a predicate that sorts the Latitude only.
Here's part of my facts.
pool(roy, -75.702744, 45.4089761).
pool(marth, -75.731638, 45.3803301).
pool(jiggy, -75.7449645, 45.40431589).
pool(yamaha, -75.7114829, 45.3993461).
I tried to do something of the following but didn't get lucky:
furthest(Lat-Long):- setof(Lat-Long, pool(_, Long, Lat), [Lat-Long|_]).
Any ideas of how I should tackle this?
does this work?
pool(roy, -75.702744, 45.4089761).
pool(marth, -75.731638, 45.3803301).
pool(jiggy, -75.7449645, 45.40431589).
pool(yamaha, -75.7114829, 45.3993461).
my_sort:-
findall(forsort(Lat,Name),pool(Name,Long,Lat),List),
msort(List,Sorted),
write(Sorted).
?- my_sort.
[forsort(45.3803301,marth),forsort(45.3993461,yamaha),forsort(45.40431589,jiggy),forsort(45.4089761,roy)]
true.
Excerpt of manual below(SWI):
msort sorts List to the standard order of terms
Standard Order of Terms:
Compound terms are first checked on their arity, then on their functor name (alphabetically) and
finally recursively on their arguments, leftmost argument first.

Fast alternative to built-in sort/4

I'm looking for an alternative to sort/4. The built-in version is currently unavailable to me.
If it was available, the syntax I need would look like this sort(1, #>=, List, Sorted). The elements in the list look like this ([Value:x:y,z:a]:[], ...). The result has to be sorted in descending order according to the Value parameter.
An example list for testing purposes:
[ [16:x:y,z:a]:[], [64:x:y,z:a]:[], [4:x:y,z:a]:[], [1024:x:y,z:a]:[], [0:x:y,z:a]:[], [100:x:y,z:a]:[], [50:x:y,z:a]:[], [-100:x:y,z:a]:[], [0:x:y,z:a]:[] ]
Just extract the key, put it in front of your structures, call sort/2 (or msort/2, to avoid losing duplicates) then strip the key from the sorted list.
The same code (a list visit) can be used to both put the key in front and remove it. The last step will reverse the list, to get descending order.
alternative_sort(Structs, Sorted) :-
structs_keyed(Structs, Keyed),
sort(Keyed, Temp),
structs_keyed(TempR, Temp),
reverse(TempR, Sorted).
structs_keyed(Sorted, Temp) :- % assume library(yall) available
maplist([S,K]>>(S=([Value|_]:_),K=Value/S), Sorted, Temp).
to be true, since the sort key value it's the first value element, could reduce to just
alternative_sort(Structs, Sorted) :-
sort(Structs, Ascending),
reverse(Ascending, Sorted).

Resources