问题
I am trying to write a small program in Prolog(gnu) that will take user input and give true or false to the question "Is this a valid sentence in this grammar?" I have had a lot of trouble finding solid documentation on Prolog, and if anyone has a reliable source that would be appreciated. From what I have found, this code for the most part should work. When I try to trace the execution, I get strange results that I do not understand.
The test case I am working with now is when the user inputs 'a = ( b + c )'. The problem is that removing the last item from my list is causing a redo loop and I am unsure why.
% Grammar:
% <assign> → <id> = <expr>
% <expr> → <id> <op> <expr>
% | ( <expr> )
% | <id>
% <op> → * | + | / | -
% <id> → a | b | c
op('+').
op('-').
op('*').
op('/').
id(a).
id(b).
id(c).
id('a').
id('b').
id('c').
id([a]).
id([b]).
id([c]).
%
% Determine if the Sentence is valid according to the grammar
%
% param (in): Sentence - A list of symbols that make up the sentence to be evaluated
%
assign(Sentence) :-
list(Sentence), length(Sentence, Length), >=(Length, 3), %precondition test
=(Sentence, [First, Second | Tail] ),
id(First),
=(Second, '='),
expr(Tail).
% Test if the list of symbols is a valid expression
expr(X) :-
length(X, Length), Length >= 1,
=(X, [First | Tail] ),
( Length = 1 ->
id(X)
; Length >= 3, =(First,'(') ->
=(Tail, [Second | Tail2] ),
last(Last, Tail),
append(Middle, [_], Tail),
=(Last, ')'),
expr(Middle)
; =(Tail, [Second | Tail2] ),
id(First),
op(Second),
expr(Tail2)
).
%
% Input a sentence, then decompose it into a list of symbols.
% User input should be quoted, e.g., Enter a sentence: 'A = B * C'
%
% param (out): SentenceList The list of symbols input by the user is bound to this variable
%
read_sentence(SentenceList) :-
print('Enter a sentence: '),
read_token(Input),
write_term_to_chars(InputList, Input, []),
delete(InputList, ' ', SentenceList).
%
% Start the program
%
start :-
read_sentence(Sentence),
assign(Sentence).
回答1:
Prolog has a syntax that make simpler symbolic processing. Here is how I would wrote the code:
assign([First, '=' | Expr]) :-
id(First),
expr(Expr).
expr([First | Tail]) :-
id(First),
right(Tail).
expr(['(' | Rest]) :-
append(Expr, [')'|Follow], Rest),
expr(Expr),
right(Follow).
right([]).
right([Op|Expr]) :-
op(Op),
expr(Expr).
note the use of pattern matching, instead of the procedural check based on length/2, etc
assign([First, '=' | Expr]) :-
...
this means: use this clause only if the argument is a list with '=' in second position and the tail is a list we name Expr.
(about syntax: often we don't need to quote atoms, but the rules are somewhat complex. For instance, this is a valid query
?- assign([c,=,a,+,b]).
No need to quote atoms here, so the code could be assign([First, = | Expr]) :- ...
)
Then the body issues the appropriate check for first list'element (i.e. id(First)) and the Expr.
To get the Expr between parenthesis, I used an idiomatic approach
expr(['(' | Rest]) :-
append(Expr, [')'|Follow], Rest),
...
this append/3 can succeed only if Rest contains an Expr followed by ')'.
I think your original approach missed the right(Follow).
We need it because the grammar is recursive, after an operator...
来源:https://stackoverflow.com/questions/21970747/prolog-looping-redos