Reputation: 539
I have a predicate which purpose is to print out which country that has the biggest area(one with biggest border = biggest area). This is how my predicate looks like:
/* If I write get_country(X, 'Europe'). then all the countries in Europe
that isn't bordering a sea gets printed out.
However as you can see I am creating a list
with all of the countries and then I want to
take the largest country from all of these
and print that one out. But instead
all of the countries gets printed out
with their length, ex: X = hungary ; 359 (length) ... */
get_country(Country, Region):-
encompasses(Country,Region,_),
not(geo_sea(_,Country,_)),
setof(Length, country_circumference(Country,Length), Cs),
largest(Cs, X),
write(X).
The predicates used within that predicate follows:
country_circumference(Country, X):-
setof(Length, get_border_length(Country, Length), Cs),
sum(Cs, X).
largest([X],X).
largest([X|Xs],R) :-
largest(Xs,Y),
R is max(X,Y).
Can anyone tell me what I am doing wrong here? How do I simply get all of my countries into the list and then traverse through the list to find the one with the biggest border instead of just printing them out one after one as I put them into the list? Thanks in advance.
Upvotes: 1
Views: 761
Reputation: 58224
Prolog defines a natural order of terms. For example, the following are true:
foo(3, z) @< foo(10, x)
bar(2, 9) @< foo(3, 1)
Note the use of the term comparison operator @<
versus the numeric comparison <
. The predicate, setof/3
, will do term comparison.
If you want to find the country that has the longest border, then you can do so by taking advantage of the term comparison and collect like terms in setof/3
that have the item you want to sort by as the first argument. In this case, we'd want the circumference first. In addition, if I'm understanding the intended meaning of your get_country
predicate correctly, you need to include the queries that define the countries you want to consider as part of the query in the setof/3
:
get_country(Country, Region):-
setof(L-C-R, X^Y^Z^( encompasses(C, R, X),
\+ geo_sea(Y, C, Z),
country_circumference(C, L) ), Cs),
reverse(Cs, HighToLowAreas),
member(_-Country-Region, HighToLowAreas), !.
The member/2
at the end of the predicate clause will find the first element in the list HighToLowAreas
that matches _-Country-Region
, which will be the first element if Country
and Region
are initially uninstantiated.
The existential quantifiers X^Y^Z^
are needed to exclude these from being selectors in the query. Using _
won't do that in the context of setof/3
. Here, we're using the term form, -(-(X,Y),Z)
since it's conveniently written, X-Y-Z
. But you could just as well use, foo(X, Y, Z)
here. The reverse/2
puts the list Cs
in descending order, and we just pick off the Country
and Region
from the head of that list with, [_-Country-Region]
.
Upvotes: 2