1
votes

I need to build a Prolog predicate which returns in Books2009 a list of books that were published in the year of 2009, and in Books2014, the books published in 2014.

setbooks: will only be used to "create" the list of books that will be used in the predicate booksList_2009_2014.
booksList_2009_2014(L, Books2009, Books2014): this is the predicate we are trying to create and run ("L" is a list of books previously set in setbooks).

My code explanation: I tried to make the predicate insertAtEnd(which would be used later, inside booksList_2009_2014) that will insert an element in the end of a list.
Then I wrote the predicate booksList_2009_2014 for the simplest case (recieving an empty list of books), then for recieving a list of books with 1 element only, and then, a recursive case that would do the job for a list of books no matter how long it is.

This is my code file:

setbooks([book('Name 5', 'Publisher ABC', date(12, 2014)), book('Name 4', 'Publisher ABC', date(05, 2009)), book('Name 3', 'Publisher ABC', date(02, 2009)), book('Name 6', 'Publisher ABC', date(03, 2013)), book('Name 2', 'Publisher ABC', date(12, 2014)), book('Name 1', 'Publisher ABC', date(06, 2009))]).

insertAtEnd(X,[ ],[X]).
insertAtEnd(X,[H|T],[H|Z]) :- insertAtEnd(X,T,Z).

booksList_2009_2014([],_,_).

booksList_2009_2014([book(Name,Publisher,date(M1,Y1))],Books2009, Books2014):-
    Y1=2014 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2014);
    Y1=2009 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2009).

booksList_2009_2014([H|T],Books2009, Books2014):-
    booksList_2009_2014(T,Books2009, Books2014).  

Im not sure, but I think the code isnt correct somewhere in the recursive code:

booksList_2009_2014([H|T],Books2009, Books2014):-
        booksList_2009_2014(T,Books2009, Books2014).  

When I type setbooks(L),booksList_2009_2014(L,Books2009, Books2014), the program is returning me just the last book of the list.. For example:

Books2009 = [book('Name 1', 'Publisher ABC', date(06, 2009))] 

Can someone help me? Pleeeasse???

3

3 Answers

1
votes

The rule

booksList_2009_2014([H|T],Books2009, Books2014):-
booksList_2009_2014(T,Books2009, Books2014).  

Keeps peeling off books one at a timeuntil you get to the rule when there is one item in the list

booksList_2009_2014([book(Name,Publisher,date(M1,Y1))],Books2009, Books2014):-
    Y1=2014 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2014);
    Y1=2009 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2009).

which of course unifies with the last book in the list, since it peels off the head continuously. Then, since you haven't made a call using the head element in the first aforemention rule, it unifies Books2009 with the one call to insertAtEnd since the year is 2009.

You need to

  • Do something with the head element you peel off (Maybe make a different predicate that takes a single book)
  • Possibly make an accumulator to pass around for your books 2009 and 2014
1
votes

How to explain ? You are reversing the CONS operation WRT the control flow... and insertAtEnd anyway it's the wrong approach in Prolog, since it's a language with immutable (assign once) variables.

You could write

booksList_2009_2014([], [], []).
booksList_2009_2014([Book|Books], L2009, L2014) :-
  booksList_2009_2014(Books, L2009_t, L2014_t), % note: construct the tail(s) first
  Book = book(_,_,date(_,Year)),
  (  Year == 2009
  -> L2009 = [Book|L2009_t], L2014 = L2014_t
  ;  Year == 2014
  -> L2009 = L2009_t, L2014 = [Book|L2014_t]
  ;  % did you forgot year 2013 ?
     L2009 = L2009_t, L2014 = L2014_t
  ).

but hardcoding data into logic it's the wrong approach in any language, and much more in Prolog. No matter how your task is simple by now, a better approach would avoid binding vars to specific years'books. Keeping it declarative:

books_of_year(AllBooks, Year, Result) :-
  Book = book(_,_,date(_,Year)),
  findall(Book, member(Book, AllBooks), Result).

and now

?- setbooks(L), books_of_year(L,2009,Books2009), books_of_year(L,2014,Books2014).
% L = ... not interesting
Books2009 = [book('Name 4', 'Publisher ABC', date(5, 2009)), book('Name 3', 'Publisher ABC', date(2, 2009)), book('Name 1', 'Publisher ABC', date(6, 2009))],
Books2014 = [book('Name 5', 'Publisher ABC', date(12, 2014)), book('Name 2', 'Publisher ABC', date(12, 2014))].
1
votes

So you are trying to select from an input list elements that satisfy one of two conditions and put them in one of two resulting lists:

% list_c1_c2_r1_r2(List, Condition1, Condition2, Result1, Result2)

Base case: your input list is empty; so the results are empty, too. Conditions can be ignored.

list_c1_c2_r1_r2([], _, _, [], []).

The head of the list satisfies condition 1:

list_c1_c2_r1_r2([X|Xs], C1, C2, [X|Ys], Zs) :-
    satisfies(X, C1),
    list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).

The head of the list satisfies condition 2:

list_c1_c2_r1_r2([X|Xs], C1, C2, Ys, [X|Zs]) :-
    satisfies(X, C2),
    list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).

The head does not satisfy either of the two conditions:

list_c1_c2_r1_r2([X|Xs], C1, C2, Ys, Zs) :-
    \+ satisfies(X, C1),
    \+ satisfies(X, C2),
    list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).

For your case, you could write satisfies(X, Condition) for example as:

satisfies(book(_, _, date(_, Year)), Year).

So, you can then query:

?- books(Books), list_c1_c2_r1_r2(Books, 2009, 2014, Book2009, Books2014).
Books = [book('Name 5', 'Publisher ABC', date(12, 2014)),
         book('Name 4', 'Publisher ABC', date(5, 2009)),
         book('Name 3', 'Publisher ABC', date(2, 2009)),
         book('Name 6', 'Publisher ABC', date(3, 2013)),
         book('Name 2', 'Publisher ABC', date(12, 2014)),
         book('Name 1', 'Publisher ABC', date(6, 2009))],
Book2009  = [book('Name 4', 'Publisher ABC', date(5, 2009)),
             book('Name 3', 'Publisher ABC', date(2, 2009)),
             book('Name 1', 'Publisher ABC', date(6, 2009))],
Books2014 = [book('Name 5', 'Publisher ABC', date(12, 2014)),
             book('Name 2', 'Publisher ABC', date(12, 2014))] .

Can you change this to work correctly for conditions that are not mutually exclusive? And what will happen if you skip the requirement in the last clause that neither condition is satisfied? How can you parametrize the way conditions are evaluated? How can you parametrize the number of conditions and resulting lists?