obtaining the rest of a clause as a list and creating a list of lists

Viewed 78

let's say that I have some clauses, two of which are the following:

has_basket('Sarah', 'bigBasket','apple','grape').
has_basket('Sarah', 'bigBasket','orange','berry').

How can I write a rule named

all_baskets(S,L).

which will give me the list of all the baskets that person S has in the following form? for example

all_baskets('Sarah',L).

will give us:

L=[['bigBasket','apple','grape], ['bigBasket','orange','berry']]
2 Answers

Whenever you consider a Prolog programming problem, you need to clarify the aspects that are important to you.

  1. Think of the order of the two elements in the list. Are they in that order because they are in alphabetical ascending order, or is it just luck?

  2. What do you expect for all_baskets('Nemo', L)? Should this fail, or rather answer L = []?

  3. What do you expect for all_baskets(P, [])?

If you say 1 alphabetical, 2 fail, and 3 fail, then setof/3 is the way to go.

has_basket('Sarah',  bigBasket, apple, grape).
has_basket('Sarah',  bigBasket, orange, berry).
has_basket('Bernie', bigBasket, orange, apple).  % another fact

all_baskets(P, L) :-
   setof([A, B, C], has_basket(P, A, B, C), L).

?- all_baskets(P, []).
   false.
?- all_baskets('Nemo', L).
   false.
?- all_baskets(P, L).
   P = 'Bernie', L = [[bigBasket,orange,apple]]
;  P = 'Sarah', L = [[bigBasket,apple,grape],[bigBasket,orange,berry]].

We can make use of the findall/3 predicate [swi-doc]:

all_baskets(S,L) :-
    findall([A, B, C], has_basket(S, A, B, C), L).

Here [A, B, C] is the "template" of the items in the list, has_basket(S, A, B, C) the "goal" that needs be fullfilled, and L the list of results.

Related