Prolog循环重做';s

Prolog循环重做';s,prolog,grammar,Prolog,Grammar,我正在尝试用Prolog(gnu)编写一个小程序,该程序将接受用户输入,并给出“这是该语法中的一个有效句子吗?”这一问题的正确或错误答案。我很难找到有关Prolog的可靠文档,如果有人有可靠的来源,我将不胜感激。从我发现的情况来看,这段代码大部分应该是有效的。当我试图追踪执行情况时,我得到了我不理解的奇怪结果 我现在处理的测试用例是当用户输入'a=(b+c')。问题是从列表中删除最后一项会导致重做循环,我不确定原因 % Grammar: % <assign> → <id>

我正在尝试用Prolog(gnu)编写一个小程序,该程序将接受用户输入,并给出“这是该语法中的一个有效句子吗?”这一问题的正确或错误答案。我很难找到有关Prolog的可靠文档,如果有人有可靠的来源,我将不胜感激。从我发现的情况来看,这段代码大部分应该是有效的。当我试图追踪执行情况时,我得到了我不理解的奇怪结果

我现在处理的测试用例是当用户输入'a=(b+c')。问题是从列表中删除最后一项会导致重做循环,我不确定原因

% 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).
%语法:
%  →  = 
%  →   
%        | (  )
%        |   
%  → * | + | / | - 
%  → a | b | c
op(+')。
op('-')。
op('*')。
op(“/”)。
同上(a)。
同上(b)。
同上(c)。
id('a')。
id('b')。
id('c')。
id([a])。
id([b])。
id([c])。
%
%根据语法判断句子是否有效
%
%param(in):句子-组成待评估句子的符号列表
%
指定(句子):-
列表(句子),长度(句子,长度),>=(长度,3),%先决条件测试
=(句子,[第一,第二|尾],
id(第一个),
=(第二个“=”),
expr(尾部)。
%测试符号列表是否为有效表达式
表达式(X):-
长度(X,长度),长度>=1,
=(X,[第一个|尾],
(长度=1->
id(X)
;长度>=3,=(第一个“(”)->
=(尾,[Second | Tail2]),
最后(最后,尾巴),
附加(中间,[[u],尾部),
=(最后一个“)”),
高级行政主任(中)
;=(尾,[Second | Tail2]),
id(第一个),
op(第二),
expr(Tail2)
).
%
%输入一个句子,然后将其分解为一系列符号。
%应引用用户输入,例如,输入一句话:“a=B*C”
%
%param(out):语句列表用户输入的符号列表绑定到此变量
%
阅读句子(句子列表):-
打印('输入句子:'),
读取令牌(输入),
将术语写入字符(输入列表,输入,[]),
删除(输入列表“”,句子列表)。
%
%启动程序
%
开始:-
读(句),,
指定(句子)。

Prolog的语法使符号处理更简单。下面是我将如何编写代码:

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).
注意使用模式匹配,而不是基于长度/2等的程序检查

assign([First, '=' | Expr]) :-
...
这意味着:仅当参数是第二个位置为“=”的列表,并且尾部是我们命名为Expr的列表时,才使用此子句

(关于语法:通常我们不需要引用原子,但规则有些复杂。例如,这是一个有效的查询

?- assign([c,=,a,+,b]).
这里不需要引用原子,因此代码可以是
assign([First,=| Expr]):-…

然后主体对第一个list元素(即id(first))和Expr发出适当的检查

为了得到括号之间的表达式,我使用了惯用的方法

expr(['(' | Rest]) :-
    append(Expr, [')'|Follow], Rest),
...
只有在Rest包含后跟“')”的Expr时,此append/3才能成功

我认为您最初的方法错过了正确的(遵循)。
我们需要它,因为语法是递归的,在一个操作符之后…

任何你不使用操作符符号作为目标的原因,也就是说
Length=1
代替
=(Length,1)
等。我在模仿课堂上教给我的东西,没有语法原因。我想知道这两种方法之间是否有显著的区别,或者是否有一种更可取。很难用函数表示法来理解。我完全同意,如果没有理由让它保持原样,我肯定会改变它。这是一个非常雄辩的解决方案,我真的很感谢你花时间来写它。您的回答也帮助我更好地理解了声明式编程语言,我为此感谢您!