2014-02-23 46 views
1

我想在Prolog(gnu)中编写一个小程序,它将接受用户输入并给出真实或错误的问题“这是这个语法中的有效句子吗?”我在寻找关于Prolog的可靠文档方面遇到了很多麻烦,如果有人有可靠的来源,我会感激。从我发现的情况来看,这个代码大部分都应该工作。当我试图追查执行情况时,我得到了我不明白的奇怪结果。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). 
+0

任何理由为什么你的目标没有用户操作的符号,那就是'长度= 1 '代替'=(长度,1)'等。 – false

+0

我模仿我在课堂上教过的东西,没有语法上的理由。我想知道这两种方法之间是否存在显着差异,或者是否有人喜欢。 – user3308726

+0

功能符号很难阅读。 – false

回答

0

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的后跟“)”

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

这个附加/ 3才能成功。

我觉得你原来的做法错过了right(Follow). 我们需要它,因为它的语法是递归的,运营商后...

+1

这是一个非常有说服力的解决方案,我真的很感激你花时间写它。您的回答也帮助我更好地理解了声明性编程语言,并且我非常感谢您! – user3308726