1
votes

I am writing a program in prolog that should interact with the user. I have a database of bands that I like and that I dislike, and I can ask prolog about these bands. First I have to say

hello.

to prolog, and prolog answers hello and I can start asking questions like

do you like the band motorhead?

and prolog should answer

yes i like the band because it is rock.

Then I should be able to ask another question.

My initial idea to achieve this was to get the last word of the question, check if it is in one of the two list (liked-list or disliked-list), and then recursively call the function to interact with the program.

In fact my code works well, except for one annoying detail that I can't solve. Here is my problem:

?- hello.
hello!
Ask your question: do you like the band motorhead?
I like the band because it is metal.
Ask your question: I don't know that band.
Ask your question: do you like the band motorhead
I like the band because it is metal.

In fact when I add a question mark at the end of the question, prolog answers the question (no problem here), recursively call the function to ask questions, and then adds "I don't know that band" and calls once again the function to ask questions instead of calling it once, and waiting for me to type another question.

Here is my current code for interacting with prolog

hello :- write('hello!'),nl, ask.
ask :- write('Ask your question: '), 
    getsentence(X), last(Word, X), 
    process(Word).

process(stop) :- !.

process(hello) :- hello, !.

process(X) :- 
  (like-list(LikeList), member(X, LikeList), type(X, Style),
   write('I like the band because it is '), write(Style), write('.'), nl
   ;
   dislike-list(DisLikeList), member(X, DisLikeList),
   write('I don\'t like that band.'), nl
   ;
   write('I don\'t know that band.'), nl),
  ask.

And here is my current code for parsing what the user types:

getsentence( Wordlist)  :-
  get0( Char),
  getrest( Char, Wordlist).

getrest( 46, [] )  :-  !.               % End of sentence: 46 = ASCII for '.'
getrest( 63, [] )  :-  !.               % 63 = ASCII for '?'
getrest( 10, [] )  :-  !.               % 10 = ASCII for '\n'
getrest( 13, [] )  :-  !.               % 13 = ASCII for 'CR'

getrest( 32, Wordlist)  :-  !,          % 32 = ASCII for blank   
 getsentence( Wordlist).                % Skip the blank 

getrest( Letter, [Word | Wordlist] )  :-
  getletters( Letter, Letters, Nextchar),   % Read letters of current word
  name( Word, Letters),
  getrest( Nextchar, Wordlist).

getletters( 46, [], 46)  :-  !.             % End of word: 46 = full stop 
getletters( 63, [], 63)  :-  !.
getletters( 10, [], 63)  :-  !.
getletters( 13, [], 63)  :-  !.

getletters( 32, [], 32)  :-  !.             % End of word: 32 = blank 

getletters( Let, [Let | Letters], Nextchar)  :-
   get0( Char),
   getletters( Char, Letters, Nextchar).

last(Item, List) :- append(_, [Item], List),!.
1

1 Answers

0
votes

Ok I managed to solve my problem by taking a different approach. I hope this will be of any use to anyone.

hello :-
  write_ln('Robot: Hello!'),
  ask.

/* read_line_to_codes converts what the user typed to ASCII codes, and then
 * with atom_codes, I reconstruct the question from the list of ASCII codes.
 */
ask :-
  write('Me: '),
  read_line_to_codes(user_input, Codes),
  atom_codes(X, Codes),
  process(X), !.

process(stop) :- !.

process(hello) :- hello.

process(X) :-
  ((sub_atom(X, _, _, _, 'do you like the band '),
   like-list(List),
   searchForLikes(X, List, Band, Style),
   write('Robot: '),
   write('I like '),
   write(Band),
   write(' because it is '),
   write(Style), write_ln('.')
  );
   (sub_atom(X, _, _, _, 'do you like the band '),
    dislike-list(List),
   searchForDislikes(X, List, Band, Style),
   write('Robot: '),
   write('I don\'t like '),
   write(Band),
   write(' because it is '),
   write(Style), write_ln('.')
  );
  (X=='what bands do you like?',
   like-list(LikeList),
   write_ln(LikeList)
  );
  (X=='what bands don\'t you like?',
   dislike-list(DislikeList),
   write_ln(DislikeList)
  );
  (sub_atom(X, _, _, _, 'do you like the band '),
   write('Robot: '),
   write_ln('I don\'t know that band.')
  );
  (write('Robot: '),
  write_ln('I don\'t understand the question.'))
  ),
  ask.

/* 
 * term_to_atom transforms for example "ac-dc" to "'ac-dc'" in order to look for that particular atom in the String. 
 * The lookup is done with the function sub_atom (it searches the Band in the string X).
 */
searchForLikes(_, [], _) :- !, fail.
searchForLikes(X, [Head|Tail], Band, Style) :-
  (term_to_atom(Head, Band),
   sub_atom(X, _, _, _, Band),
   type(Head, Style),
   !);
  searchForLikes(X,Tail, Band, Style).

searchForDislikes(_, [], _) :- !, fail.
searchForDislikes(X, [Head|Tail], Band, Style) :-
  (term_to_atom(Head, Band),
   sub_atom(X, _, _, _, Band),
   type(Head, Style),
   !);
  searchForDislikes(X,Tail, Band, Style).

The main difference of this code with the first one is the use of built in functions.