简体   繁体   中英

GNU Prolog - searching a list of facts

I must be having a brain fart or something, but i just can't seem to find a solution to this.

If you have a list facts such as:

%country(country, population, capital)
country(sweden, 8823, stockholm).
country(usa, 221000, washington).
country(france, 56000, paris).
country(denmark, 3400, copenhagen).

%city(city, country, population)
city(lund, sweden, 88).
city(new_york, usa, 5000).
city(paris, usa, 1).
city(copenhagen, denmark, 1200).
city(aarhus, denmark, 330).
city(odense, denmark, 120).
city(stockholm, sweden, 350).
city(washington, usa, 3400).
city(paris, france, 2000).
city(marseilles, france, 1000).

I want to find the second largest populated city, which in this case would be washington, usa with 3400 people. How would you be able to do this?

Thanks.

Try this out for size:

second_largest_city(City) :-
    findall(Size, city(_, _, Size), Sizes),
    sort(Sizes, SortedSizes),
    append(_, [Size2, _], SortedSizes),
    city(City, _Country, Size2).

Explanation: The findall/3 finds the sizes of all city/3 facts, which are sorted into ascending order by sort/2 with duplicates removed . The call to append/3 pattern matches to partition the sorted list SortedSizes into two parts; a list of any size ( _ ) and a remainder of length two ( [Size2, _] ) - this binds the variable Size2 to the second-largest city size from city/3 facts. Lastly, all cities with this size are located amongst city/3 facts, and are bound on the output.

Note: This won't work properly in general if your built-in for sort/2 doesn't remove duplicates, because this leaves open the possibility that city/3 facts with more than one equal maximum will return the maximum (largest) only. This implementation using append/3 to seek the second-last element of the sorted list of sizes also assumes sort/2 sorted numbers into ascending order.

Also, lastly, note that this will fail outright if there are less than two city/3 facts -- but this is probably fine, given that the predicate seeks the 'second largest' city, and strictly speaking there wouldn't be one unless there are indeed at least two cities in the DB with different sizes. If this is an issue, you can just write more clauses for second_largest_city/1 to handle such a case.

Slightly shorter version of @sharky's excellent answer:

second_largest_city(Second) :-
    setof(Size/City, Country^city(City,Country,Size), Cities),
    append(_, [_/Second, _], Cities).

setof combines findall and sort . We collect Size/City pairs so they are sorted on size automatically. The construct X^Goal introduces an existentially quantified variable X (like ∃ x in first-order logic).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM