Prolog计算器只返回true

时间:2014-04-19 14:02:13

标签: prolog dcg clpfd

我在Prolog中编写了一个计算器,用于阅读自然语言问题,并为课堂作业返回一个数字答案,而且我几乎完成了。但是,当我输入一个句子时,程序只返回“是”'然后退出。据我所知,它甚至没有在句子中读到。这是我第一次在Prolog写作,所以我不知道出了什么问题。任何帮助将不胜感激。

我的代码:

:- consult('aux.p').

accumulator(0).

start :- 
    write('Cranky Calculator'), nl, 
    write('-----------------'), nl, 
    cvt.

cvt :- 
    write('What do ya want?'), nl, 
    read_sentence(Question), 
    butlast(Question, Questio), 
    Questio \== [quit], !,
    ( 
        phrase(sentence(Value), Questio, []),
        write(Value); 
        write_string('Stop it with your gibberish!') 
    ), nl, 
    cvt.  

cvt.

reset(V) :-
    retract(accumulator(_)),
    assert(accumulator(V)).

accumulate('plus', N, Value) :-
    {Temp is accumulator(_)},
    {Value is Temp + N},
    reset(Value).
accumulate('minus', N, Value) :-
    {Temp is accumulator(_)},
    {Value is Temp - N},
    reset(Value).
accumulate('divided', N, Value) :-
    {Temp is accumulator(_)}, 
    {Value is Temp / N}, 
    reset(Value).
accumulate('times', N, Value) :-
    {Temp is accumulator(_)}, 
    {Value is Temp * N},
    reset(Value). 

accumulate(N1, 'plus', N2, Value) :-
    {Value is N1 + N2},
    reset(Value).
accumulate(N1, 'minus', N2, Value) :-
    {Value is N1 - N2},
    reset(Value).    
accumulate(N1, 'divided', N2, Value) :-
    {Value is N1 / N2},
    reset(Value).
accumulate(N1, 'times', N2, Value) :-
    {Value is N1 * N2},
    reset(Value).

%------------------base productions---------------------

% sentence can be to an entirely new question or simply be an addition
% to the previous one
sentence(V) --> base(V1), {V is V1}.
sentence(V) --> additional(V1), {V is V1}.
sentence --> [].

base(Value) -->
   pro, be, number(N1), oper(OP), number(N2), qmark,
   {
      accumulate(N1, OP, N2, V), {Value is V}
   }.

additional(Value) -->
   oper(OP), number(N), qmark,
   {
      accumulate(OP, N, V), {Value is V} 
   }.

pro --> [what].
pro --> [how], [much].

be --> [is].

number(N) --> five_digit(N1), {N is N1}.

five_digit(N) --> ten_thousands(V1), four_digit(V2), {N is 1000 * V1 + V2}.
four_digit(N) --> thousands(V1), three_digit(V2), {N is 1000 * V1 + V2}.
three_digit(N) --> hundreds(V1), two_digit(V2), {N is 100 * V1 + V2}.
two_digit(N) --> tens(V1), one_digit(V2), {N is V1 + V2}.
two_digit(N) --> teens(V), {N is V}.
one_digit(N) --> digit(V), {N is V}. 
one_digit(0) --> [].

ten_thousands(T) --> tens(V), thousand, {T is V}.
ten_thousands(T) --> tens(V), {T is V}.
ten_thousands(T) --> teens(V), thousand, {T is V}.
ten_thousands(0) --> [].

thousands(T) --> digit(V), thousand, {T is V}.
thousands(0) --> [].

hundreds(T) --> digit(V), hundred, {T is V}.
hundreds(0) --> [].

thousand --> [thousand].
hundred --> [hundred].

digit(1) --> [one].
digit(2) --> [two].
digit(3) --> [three].
digit(4) --> [four].
digit(5) --> [five].
digit(6) --> [six].
digit(7) --> [seven].
digit(8) --> [eight].
digit(9) --> [nine].

tens(20) --> [twenty].
tens(30) --> [thirty].
tens(40) --> [fourty].
tens(50) --> [fifty].
tens(60) --> [sixty].
tens(70) --> [seventy].
tens(80) --> [eighty].
tens(90) --> [ninety].

teens(10) --> [ten].
teens(11) --> [eleven].
teens(12) --> [twelve].
teens(13) --> [thirteen].
teens(14) --> [fourteen].
teens(15) --> [fifteen].
teens(16) --> [sixteen].
teens(17) --> [seventeen].
teens(18) --> [eighteen].
teens(19) --> [nineteen]. 

oper(plus) --> [plus].
oper(plus) --> [and].
oper(minus) --> [minus].
oper(divided) --> ['divided by'].
oper(times) --> [times].

qmark --> ['?'].

我得到的输出如下:

|: what is twelve plus two?

Yes

1 个答案:

答案 0 :(得分:1)

我把你的代码作为计算器的规范,也提供了 结果为文本。这里的想法是结合DCG和CLP(FD)。 CLP(FD)是有限域的约束求解。有限域 应该足够你的计算器。要启用CLP(FD) 首先加载适当的库。在Jekejeke Minlog中 完成如下:

:- ensure_loaded(library('clpfd.px')).

代码首先有一个不仅可以识别数字的部分 但也为数字生成文本。这主要是在哪里 DCG与CLP(FD)组合:

number(N) --> {N #= 1000 * V1 + 100 * V2 + V3}, thousands(V1), 
              hundreds(V2), two_digit_opt(V3).

thousands(N) --> two_digit(N), thousand.
thousands(0) --> [].

thousand --> [thousand].

hundreds(N) --> digit(N), hundred.
hundreds(0) --> [].

hundred --> [hundred].

two_digit_opt(N) --> two_digit(N).
two_digit_opt(0) --> [].

two_digit(N) --> {N #= V1*10 + V2}, tens(V1), digit_opt(V2).
two_digit(N) --> {N #= V+10}, teens(V).
two_digit(N) --> digit(N).

digit_opt(N) --> digit(N).
digit_opt(0) --> [].

digit(1) --> [one].
digit(2) --> [two].
digit(3) --> [three].
digit(4) --> [four].
digit(5) --> [five].
digit(6) --> [six].
digit(7) --> [seven].
digit(8) --> [eight].
digit(9) --> [nine].

tens(2) --> [twenty].
tens(3) --> [thirty].
tens(4) --> [fourty].
tens(5) --> [fifty].
tens(6) --> [sixty].
tens(7) --> [seventy].
tens(8) --> [eighty].
tens(9) --> [ninety].

teens(0) --> [ten].
teens(1) --> [eleven].
teens(2) --> [twelve].
teens(3) --> [thirteen].
teens(4) --> [fourteen].
teens(5) --> [fifteen].
teens(6) --> [sixteen].
teens(7) --> [seventeen].
teens(8) --> [eighteen].
teens(9) --> [nineteen].

以下证明双向性有效:

?- phrase(number(X),[fifty,five]).
X = 55 ;
No
?- phrase(number(55),X).
X = [fifty,five] ;
No

添加计算器很简单。我没有使用断言/收回, 我只是在无限循环中使用一个参数。我不知道健康状况如何 这是为你的Prolog系统,特别是因为我们现在介于触摸之间 约束存储。至少在Jekejeke Minlog 0.7.2版本中 约束存储将不会被完全回收,因此一个 无法无限期地运行循环。

但要展示如何将所有部分放在一起,循环解决方案 很好。代码如下:

loop(S) :-
   write('> '),
   flush_output,
   read(L),
   phrase(cmd(C),L),
   do(C,S,T),
   phrase(number(T),M),
   write(M), nl,
   !, loop(T).
loop(S) :-
   write('?'), nl,
   loop(S).

do(set(N),_,N).
do(add(N),S,T) :- T is S+N.
do(sub(N),S,T) :- T is S-N.

cmd(set(N)) --> factor(N).
cmd(add(N)) --> [plus], factor(N).
cmd(sub(N)) --> [minus], factor(N).

factor(M) --> number(N), more(N, M).

more(N, M) --> [times], number(H), {J is N*H}, more(J,M).
more(N, M) --> [divided, by], number(H), {J is N//H}, more(J,M).
more(N, N) --> [].

以下是执行示例:

?- loop(0).
> [eleven,times,eleven].
[one,hundred,twenty,one]
> [minus,sixty,six].
[fifty,five]

以下是Jekejeke CLP(FD)

的一些内容

Jekejeke Minlog桌面安装
https://www.youtube.com/watch?v=6ZipaIrxSFQ

Jekejeke Minlog Android安装
https://www.youtube.com/watch?v=Y2P7cEuOIws