【问题标题】:Prolog Looping Redo'sProlog 循环重做
【发布时间】:2014-02-23 16:10:30
【问题描述】:

我正在尝试在 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).

【问题讨论】:

  • 任何你不为目标使用操作符的原因,即Length = 1 代替=(Length,1) 等。
  • 我在模仿我在课堂上教的东西,没有语法原因。我想知道这两种方法之间是否存在显着差异,或者是否首选一种。
  • 函数式表示法很难阅读。
  • 我绝对同意,如果没有理由让它保持现状,我一定会改变它。

标签: prolog grammar


【解决方案1】:

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'element(即 id(First))和 Expr 发出适当的检查。

为了获得括号之间的 Expr,我使用了惯用的方法

expr(['(' | Rest]) :-
    append(Expr, [')'|Follow], Rest),
...

只有当 Rest 包含一个 Expr 后跟 ')' 时,此 append/3 才能成功。

我认为您最初的方法错过了right(Follow). 我们需要它,因为语法是递归的,在运算符之后......

【讨论】:

  • 这是一个非常有说服力的解决方案,非常感谢您花时间编写它。您的回答也帮助我更好地理解了声明式编程语言,对此我表示感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多