Input in Prolog
Asked Answered
M

4

8

I'm currently working on a recursive Prolog program to link routes together to create a basic GPS of the Birmingham area. At the moment I can get output as so:

Input

routeplan(selly_oak, aston, P).

Output

P = [selly_oak, edgbaston, ... , aston]

What I would like to do is have my program provide some sort of interface, so if I were to type in something along the lines of:

Route from selly_oak to aston

It would provide me with:

Go from selly_oak to edgbaston
Go from edgbaston to ...
Finally, Go from ... to aston.

Prolog is a powerful language so I assume this is easily possible, however many of the books I've taken out seem to skip over this part. As far as I am aware I have to use something along the lines of write() and read() although the details are unknown to me.

Could anyone here a Prolog novice out with some basic examples or links to further information?

EDIT: A lot of these answers seem very complicated, where the solution should only be around 5-10 lines of code. Reading in a value isn't a problem as I can do something along the lines of:

find:- 
    write('Where are you? '), 
    read(X), 
    nl, write('Where do you want to go? '),
    read(Y), 
    loopForRoute(X,Y).

I'd prefer it if the output could be written out using write() so a new line (nl) can be used, so that it displays like the output above.

If this were my input, how would I then arrange the top routeplan() to work with these inputs? Also, if I were to add the Lines for these stations as an extra parameter how would this then be implemented? All links are defined at the beginning of the file like so:

rlinks(selly_oak, edgbaston, uob_line).
rlinks(edgbaston, bham_new_street, main_line).

Therefore, with this information, it'd be good to be able to read the line as so.

Go from selly_oak to edgbaston using the uob_line
Go from edgbaston to ... using the ...
Finally, go from ... to aston using the astuni_line
Mulhouse answered 24/3, 2009 at 9:45 Comment(3)
What Prolog variety are you using?Dubbin
Is rlinks/3 functional? I.e. does it map a pair of "stations" to a single "line"? Or is the "line" information now delivered by routeplan/3 somehow?Armidaarmiger
Yes, rlinks/3 will be functional and it maps stations to lines. Stations may also appear on more than one line.Mulhouse
B
2

For this sort of thing, I usually create shell predicates. So in your case...

guided:-
    print('Enter your start point'),nl,
    read(Start),
    print('Enter your destination'),nl,
    read(Dest),
    routeplan(Start, Dest, Route),
    print_route(Route).

And print_route/1 could be something recursive like this:

print_route([]).

print_route([[A,B,Method]|Tail]):-
    print_route(Tail),
    print('Go from '), print(A),
    print(' to '), print(B),
    print(' by '), print(Method), nl.

I've assumed that the 3rd variable of the routeplan/3 predicate is a list of lists. Also that it's built by adding to the tail. If it's not, it should be fairly easy to adapt. Ask in the comments.

Backing answered 2/4, 2009 at 18:14 Comment(8)
Why "list of lists"? If you know that the inner list has always 3 elements then use a non-list term there, i.e. [link(A, B, Method) | Rest].Armidaarmiger
1) You were correct about guided and I've removed the parentheses. 2) A list of lists is easier to build generatively. On the other hand, if you had the search space defined explicitly, then you could use your approach. 3) That's just preference - they're functionally the same.Backing
Not sure why this was voted down. Based on the way the question reads at the moment, I'd say this answer is quite close to what the asker is looking for.Oboe
This is what I was requesting and it is a great explanation. Thank you!Mulhouse
@Tom Wright, I don't really understand your response to (2). Also, why did you make the assumption that the input to print_route/1 is in the reversed order, i.e. the first step to take is the last element in the list. This seems pretty weird.Armidaarmiger
@Armidaarmiger My response to 2 was meant to explain the fact that if it was a generative search (like you might find in GPS), the problem space would not be explicitly mapped and so using the list would avoid using assert (which messes with backtracking).Backing
@Armidaarmiger Also, I assumed it would be reversed because I would have build on top of the tail on the way into the recursive search. If you favour a different method then my print_route/2 predicate is easy to adapt. Hope that helps.Backing
@Tom Wright, I did not propose any assert! What I meant was that I wouldn't use a list to represent a triple. E.g. in Prolog one shouldn't use a list to represent a pair (i.e. -(a, b) is to be preferred to .(a, .(b, [])). Same goes for triple: don't use [a, b, c], use triple(a, b, c).Armidaarmiger
A
5

A book which discusses such things in detail is Natural Language Processing for Prolog Programmers by Michael A. Covington.

In general, what you need to do is

  1. Tokenize the input
  2. Parse the tokens (e.g. with DCG) to get the input for routeplan/3
  3. Call routeplan/3
  4. Generate some English on the basis of the output of routeplan/3

Something like this (works in SWI-Prolog):

% Usage example:
%
% ?- query_to_response('Route from selly_oak to aston', Response).
%
% Response = 'go from selly_oak to edgbaston then go from edgbaston
%         to aston then stop .'
%
query_to_response(Query, Response) :-
    concat_atom(QueryTokens, ' ', Query), % simple tokenizer
    query(path(From, To), QueryTokens, []),
    routeplan(From, To, Plan),
    response(Plan, EnglishTokens, []),
    concat_atom(EnglishTokens, ' ', Response).

% Query parser
query(path(From, To)) --> ['Route'], from(From), to(To).
from(From) --> [from], [From], { placename(From) }.
to(To) --> [to], [To], { placename(To) }.

% Response generator
response([_]) --> [stop], [.].
response([From, To | Tail]) -->
    goto(path(From, To)), [then], response([To | Tail]).
goto(path(From, To)) --> [go], from(From), to(To).

% Placenames
placename(selly_oak).
placename(aston).
placename(edgbaston).

% Mock routeplan/3
routeplan(selly_oak, aston, [selly_oak, edgbaston, aston]).
Armidaarmiger answered 24/3, 2009 at 11:45 Comment(1)
NLP in Prolog brings back so many late-night college memories. sighPedraza
D
4

Hm, if I understand you correctly you just want to format the list nicely for printing out, no?

In SWI-Prolog this works:

output_string([A,B],StrIn,StrOut) :-
 concat_atom([StrIn, 'Finally, Go from ', A, ' to ', B, '.'],StrOut),
 write(StrOut).

output_string([A,B|Rest],StrIn,StrOut) :-
 concat_atom([StrIn,'Go from ', A, ' to ', B, '.\n'],StrAB),
 output_string([B|Rest],StrAB,StrOut).

then call with

output_string(P,'',_).

It's probably not very efficient, but it does the job. :)

Dubbin answered 24/3, 2009 at 10:47 Comment(1)
The only thing I'd do differently is keep a list of lines instead of concatenating it altogether into one string. It gives more flexibility in how you can use it later.Benghazi
B
2

For this sort of thing, I usually create shell predicates. So in your case...

guided:-
    print('Enter your start point'),nl,
    read(Start),
    print('Enter your destination'),nl,
    read(Dest),
    routeplan(Start, Dest, Route),
    print_route(Route).

And print_route/1 could be something recursive like this:

print_route([]).

print_route([[A,B,Method]|Tail]):-
    print_route(Tail),
    print('Go from '), print(A),
    print(' to '), print(B),
    print(' by '), print(Method), nl.

I've assumed that the 3rd variable of the routeplan/3 predicate is a list of lists. Also that it's built by adding to the tail. If it's not, it should be fairly easy to adapt. Ask in the comments.

Backing answered 2/4, 2009 at 18:14 Comment(8)
Why "list of lists"? If you know that the inner list has always 3 elements then use a non-list term there, i.e. [link(A, B, Method) | Rest].Armidaarmiger
1) You were correct about guided and I've removed the parentheses. 2) A list of lists is easier to build generatively. On the other hand, if you had the search space defined explicitly, then you could use your approach. 3) That's just preference - they're functionally the same.Backing
Not sure why this was voted down. Based on the way the question reads at the moment, I'd say this answer is quite close to what the asker is looking for.Oboe
This is what I was requesting and it is a great explanation. Thank you!Mulhouse
@Tom Wright, I don't really understand your response to (2). Also, why did you make the assumption that the input to print_route/1 is in the reversed order, i.e. the first step to take is the last element in the list. This seems pretty weird.Armidaarmiger
@Armidaarmiger My response to 2 was meant to explain the fact that if it was a generative search (like you might find in GPS), the problem space would not be explicitly mapped and so using the list would avoid using assert (which messes with backtracking).Backing
@Armidaarmiger Also, I assumed it would be reversed because I would have build on top of the tail on the way into the recursive search. If you favour a different method then my print_route/2 predicate is easy to adapt. Hope that helps.Backing
@Tom Wright, I did not propose any assert! What I meant was that I wouldn't use a list to represent a triple. E.g. in Prolog one shouldn't use a list to represent a pair (i.e. -(a, b) is to be preferred to .(a, .(b, [])). Same goes for triple: don't use [a, b, c], use triple(a, b, c).Armidaarmiger
W
0

Here are a few predicates to read lines from a file/stream into a Prolog string:

%%% get_line(S, CL): CL is the string read up to the end of the line from S.
%%% If reading past end of file, returns 'end_of_file' in CL first, raises
%%% an exception second time.
%%% :- pred get_string(+stream, -list(int)).
get_line(S, CL) :-
    peek_code(S, C),
    (   C = -1
    ->  get_code(S, _),
        CL = end_of_file
    ;   get_line(S, C, CL)).

get_line(_, -1, CL) :- !, CL = [].  % leave end of file mark on stream
get_line(S, 0'\n, CL) :- !,
    get_code(S, _),
    CL = [].
get_line(S, C, [C|CL]) :-
    get_code(S, _),
    peek_code(S, NC),
    get_line(S, NC, CL).

%% read_lines(L): reads lines from current input to L.  L is a list of list
%% of character codes, newline characters are not included.
%% :- pred read_lines(-list(list(char))).
read_lines(L) :-
    current_input(In),
    get_line(In, L0),
    read_lines(In, L0, L).

%% read_lines(F, L): reads lines from F to L.  L is a list of list of character
%% codes, newline characters are not included.
%% :- pred read_lines(+atom, -list(list(char))).
read_lines(F, L) :-
    fail_on_error(open(F, read, S)),
    call_cleanup((get_line(S, L0),
              read_lines(S, L0, L)),
             close(S)).

read_lines(_, end_of_file, L) :- !, L = [].
read_lines(S, H, [H|T]) :-
    get_line(S, NH),
    read_lines(S, NH, T).

Then, take a look at DCGs for information on how to parse a string.

Whelk answered 24/3, 2009 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.