I am trying to write a predicate with longest land border using some help from Prolog, finding largest value from a setOf list. The issue I am facing here is the generated output is not what I am expected.
The current output from my code generated descending order by 'circumference' of all the countries and continents. But I need only one entry (with highest circumference) from each continent and secondly countries that are located in more than one continent can not be included in the output.
my code:
lb_country(Continent, Country):-
setof(L-R-C, X^Y^Z^( encompasses(C, R, X),
\+ geo_sea(Y, C, Z),
circumference(L, C) ), Cs),
reverse(Cs, HighToLowAreas),
member(_-Continent-Country, HighToLowAreas).
Included predicate circumference is defined as
borders_sym(X,Y,L) :- borders(X,Y,L);borders(Y,X,L).
bord(Country,L) :- borders_sym(_,C,L), Country = C.
circumference(C, Country) :-
setof(P, bord(Country,P), List),sum_list(List,C).
example of encompasses and geo_sea
% encompasses(C,R,Fr), country C is encompassed by region R to fraction Fr (percent)
encompasses(austria,'Europe',100).
encompasses(austria,'Europe',100).
encompasses(afghanistan,'Asia',100).
encompasses(antigua_and_barbuda,'America',100).
encompasses(albania,'Europe',100).
encompasses(american_samoa,'Australia/Oceania',100).
encompasses(andorra,'Europe',100).
encompasses(angola,'Africa',100).
encompasses(armenia,'Asia',100).
encompasses(aruba,'America',100).
encompasses(australia,'Australia/Oceania',100).
encompasses(anguilla,'America',100).
encompasses(azerbaijan,'Asia',100).
encompasses(bangladesh,'Asia',100).
encompasses(barbados,'America',100).
encompasses(benin,'Africa',100).
encompasses(bermuda,'America',100).
encompasses(belgium,'Europe',100).
encompasses(burkina_faso,'Africa',100).
encompasses(pitcairn_islands,'Australia/Oceania',100).
encompasses(pakistan,'Asia',100).
encompasses(poland,'Europe',100).
encompasses(papua_new_guinea,'Australia/Oceania',100).
encompasses(puerto_rico,'America',100).
encompasses(paraguay,'America',100).
encompasses(qatar,'Asia',100).
encompasses(argentina,'America',100).
encompasses(russia,'Asia',75).
encompasses(botswana,'Africa',100).
encompasses(central_african_republic,'Africa',100).
encompasses(taiwan,'Asia',100).
encompasses(congo,'Africa',100).
encompasses(chile,'America',100).
encompasses(reunion,'Australia/Oceania',100).
encompasses(russia,'Europe',25).
% geo_sea(N,C,P), the sea N is in country C in province P
geo_sea('Andaman Sea',india,'Andaman and Nicobar Is.').
% geo_sea(N,C,P), the sea N is in country C in province P
geo_sea('Andaman Sea',india,'Andaman and Nicobar Is.').
geo_sea('Andaman Sea',myanmar,'Ayeyarwady').
geo_sea('Andaman Sea',myanmar,'Bago').
geo_sea('Andaman Sea',myanmar,'Mon').
geo_sea('Andaman Sea',myanmar,'Yangon').
geo_sea('Andaman Sea',indonesia,'Aceh').
geo_sea('Andaman Sea',thailand,'Thailand').
geo_sea('Arabian Sea',india,'Goa').
geo_sea('Arabian Sea',india,'Gujarat').
geo_sea('Arabian Sea',india,'Karnataka').
geo_sea('Arabian Sea',india,'Kerala').
geo_sea('Arabian Sea',india,'Lakshadweep Is.').
geo_sea('Arabian Sea',india,'Maharashtra').
geo_sea('Arabian Sea',oman,'Oman').
geo_sea('Arabian Sea',pakistan,'Balochistan').
geo_sea('Arabian Sea',pakistan,'Sindh').
geo_sea('Arctic Ocean',canada,'Northwest Territories').
borders predicate
% borders(X,Y,L), country X borders country Y, the border is L kilometers
borders(austria,switzerland,164).
borders(austria,czech_republic,362).
borders(austria,germany,784).
borders(afghanistan,china,76).
borders(afghanistan,iran,936).
borders(afghanistan,pakistan,2430).
borders(afghanistan,tajikistan,1206).
borders(afghanistan,turkmenistan,744).
borders(afghanistan,uzbekistan,137).
borders(austria,liechtenstein,37).
borders(austria,hungary,366).
borders(austria,italy,430).
borders(albania,greece,282).
borders(albania,kosovo,112).
borders(albania,macedonia,151).
borders(albania,montenegro,172).
borders(andorra,spain,65).
borders(andorra,france,60).
borders(angola,namibia,1376).
For circumference/2, you can write:
circumference(Country, C) :-
findall(Km, ( border(Country, _, Km) ; border(_, Country, Km) ), Kms),
sum_list(Kms, C).
The table encompasses/3 has the full list of countries? Then, you could define "country entirely on one continent" as: encompasses(Country, _, 100)
?
To group by continents, and only list contries entirely on the continent, you can do:
?- bagof(Country, encompasses(Country, Continent, 100), Countries).
Continent = 'Africa',
Countries = [angola, benin, burkina_faso] ;
Continent = 'America',
Countries = [antigua_and_barbuda, aruba, anguilla, barbados, bermuda] ;
Continent = 'Asia',
Countries = [afghanistan, armenia, azerbaijan, bangladesh] ;
Continent = 'Australia/Oceania',
Countries = [american_samoa, australia] ;
Continent = 'Europe',
Countries = [austria, albania, andorra, belgium].
You can now do as in your question: add the circumference, order by it, reverse, take the first element:
longest_border(Continent, Country, Length) :-
bagof(N-C,
( encompasses(C, Continent, 100),
circumference(C, N)
),
Cs),
keysort(Countries, S),
reverse(S, [X-Y|_]).
But you have a lot of data missing. I get:
?- longest_border(Continent, Country, Length).
Continent = 'Africa',
Country = angola,
Length = 1376 ;
Continent = 'America',
Country = bermuda,
Length = 0 ;
Continent = 'Asia',
Country = afghanistan,
Length = 5529 ;
Continent = 'Australia/Oceania',
Country = australia,
Length = 0 ;
Continent = 'Europe',
Country = austria,
Length = 2143.