Namit
Namit

Reputation: 1322

Sorting atoms in prolog

I have atoms of the following format in my knowledge base:

car_details(bmw, [1, 3, 5], 40000).
car_details(audi, [1, 3, 4, 5, 6, 8], 60000).
car_details(volvo, [40, 60, 80, 90], 50000).
car_details(mercedes, [a, b, c, e, s], 80000).

I am trying to sort these atoms based on their costs (3rd position values). Is there a built in predicate which can allow me to do that? I've tried using sort/2 with the findall predicate. However, that only results in a sorted list of all the costs. I want the associated information with it too.

The expected result from the above should be of the following format:

[[bmw, [1, 3, 5], 40000], [volvo, [40, 60, 80, 90], 50000], [audi, [1, 3, 4, 5, 6, 8], 60000], [mercedes, [a, b, c, e, s], 80000]].

Upvotes: 2

Views: 422

Answers (2)

CapelliC
CapelliC

Reputation: 60024

predsort/3 could work, after introducing comp_prices/3:

comp_prices(R,[_,_,P1],[_,_,P2]) :- compare(R,P1,P2).

?- findall([M,D,P],car_details(M,D,P),L), predsort(comp_prices,L,S).
L = [[bmw, [1, 3, 5], 40000], [audi, [1, 3, 4, 5, 6|...], 60000], [volvo, [40, 60, 80, 90], 50000], [mercedes, [a, b, c|...], 80000]],
S = [[bmw, [1, 3, 5], 40000], [volvo, [40, 60, 80, 90], 50000], [audi, [1, 3, 4, 5|...], 60000], [mercedes, [a, b, c|...], 80000]].

keysort/2 is more efficient, with library(pairs) it's also handy to use:

4 ?- findall(P-[M,D,P],car_details(M,D,P), L), keysort(L, S), pairs_values(S, T).
L = [40000-[bmw, [1, 3, 5], 40000], 60000-[audi, [1, 3, 4, 5|...], 60000], 50000-[volvo, [40, 60, 80|...], 50000], 80000-[mercedes, [a, b|...], 80000]],
S = [40000-[bmw, [1, 3, 5], 40000], 50000-[volvo, [40, 60, 80, 90], 50000], 60000-[audi, [1, 3, 4|...], 60000], 80000-[mercedes, [a, b|...], 80000]],
T = [[bmw, [1, 3, 5], 40000], [volvo, [40, 60, 80, 90], 50000], [audi, [1, 3, 4, 5|...], 60000], [mercedes, [a, b, c|...], 80000]].

Upvotes: 1

lurker
lurker

Reputation: 58254

I don't think there's one predicate to do it, but a simple solution might be:

reorder([], []).
reorder([[P,M,I]|W], [[M,I,P]|W1]) :- reorder(W, W1).

cars_by_price(Result) :-
    setof([Price, Make, Items], car_details(Make, Items, Price), L),
    reorder(L, Result).

The setof predicate will remove duplicates, so I'm assuming that your sets of data are unique. If not, you may need to do the findall and do a sort right before the reorder.

Upvotes: 1

Related Questions