【问题标题】:Postfix expression list evaluation后缀表达式列表求值
【发布时间】:2013-04-03 12:55:30
【问题描述】:

我编写了一个程序来从表达式列表中递归地评估 prolog 中的后置表达式。例如,给定以下列表:

[+,1,2]

它应该返回 3。我构造谓词的方式是递归调用自身,直到它到达列表的末尾,以便它向后读取值。 (与从左到右阅读此列表相同:[2,1,+])。

我的问题是,当我尝试通过递归调用返回多个值时,所有值都会突然消失。

代码如下:

eval_list([Head|Tail],_,Result):-
   Tail==[], % last element of list
   Result=Head,
   write(Head),
   write(' was stored in Result!\n').

eval_list([Head|Tail],Store1,Result):-
      eval_list(Tail,Store2, NewResult),
      (\+integer(Store2))
   ->
      % if no integer is bound to Store2, bind Store1 to Head
      Store1=Head,
      Result is NewResult,
      write(Head),
      write(' is stored value!\n')
   ;  (integer(Store2)) ->
    % if an integer is bound to store2, we perform operation specified by the Head with the stored number
      X is Store2+NewResult,
      Result is X,
      write('performed operation!\n')
   ;
      % if doesnt catch either of these states the program is broken
      (  print('something broke\n'),
         print(Store1),
         nl,
         print(Store2),
         nl,
         print(Head),
         nl,
         print(Result),
         nl
      ).

我得到以下输出:

?- eval_list([+,1,2],X,Result).
2 was stored in Result!
1 is stored value!
something broke
_G1162
_L147
+
_G1163
true.

我不明白为什么我的价值观会消失,或者是否有更好的方法来评估列表。

【问题讨论】:

    标签: list recursion prolog tail-recursion postfix-notation


    【解决方案1】:

    关于您的编程技术的一些建议。您应该在谓词定义和 if-else 构造的主体中使用头部匹配和统一而不是显式统一。您还应该避免非尾递归递归,除非您的算法本质上是递归的(例如,按顺序遍历树)。这将使代码更容易编写、阅读和理解。现在,我什至不想调试您的代码,但看起来您的 Store2 永远不会绑定到整数,并且始终是未绑定的变量,无论您的程序有什么输入。

    现在开始你的程序。目前尚不清楚您要达到的目标。如果你只想评估[Arithmetic_operator, Operand1, Operand2] 形式的列表,写起来会容易得多:

    arith_eval(Expression_list, Result) :-
        Arithmetic_expr =.. Expression_list, % look up what =.. stands for!
        Result is Arithmetic_expr.
    

    我认为您不需要这种过于复杂的方法。

    如果您希望能够评估任意复杂的表达式,用 post-fix 编写,具有固定的运算符数量(所以您可以说 2, 3, +,但不能说 2, 4, 1, +,总和为 7):

    • 从输入中读取一个元素
      • 将元素推到栈顶
      • 尝试减少堆栈:
        • 弹出操作符和操作数,如果在栈顶
        • 评估
        • 将结果推回堆栈顶部
    • 当输入为空时,你的堆栈就是你的结果

    您可以像这样显式定义不同运算符(这里只有+-)的效果:

    eval_stack([+,A,B|Tail],[Result|Tail]) :-
        number(A), number(B),
        !,
        Result is B + A.
    eval_stack([-,A,B|Tail],[Result|Tail]) :-
        number(A), number(B),
        !,
        Result is B - A.
    eval_stack(Stack,Stack).
    

    注意运算符如何匹配堆栈顶部,并在其下方有操作数时应用,将结果推回堆栈,或堆栈保持不变。

    你可以从你的输入推送到你的堆栈:

    evaluate([Next|Rest], Stack, Result) :-
        eval_stack([Next|Stack],NewStack),
        evaluate(Rest,NewStack,Result).
    evaluate([],Result,Result). % end of input
    

    所以现在你可以这样称呼它:

    ?- evaluate([2,3,+,3,6,-,+],[],Result).
    Result = [2].
    
    ?- evaluate([2,3,4,-,-,5,+],[],Result).
    Result = [8].
    
    ?- evaluate([2,3,4,-,-,5,+,1,3,2,-],[],Result).
    Result = [1,1,8].
    

    所以这两个谓词evaluate(Input,Stack,Result)eval_stack(Stack,NewStack) 是您只需要使用固定数量运算符评估有效的后缀算术表达式。

    【讨论】:

    • 我试图做的方法是反转后缀表达式,以便我的递归程序可以从右到左向后读取它:P 我给出的例子是让事情变得简单哈哈。我希望能够处理任何大小的表达式。据我了解,您的程序版本会不断重新组织表达式,直到它匹配其中一个 eval_stack 谓词,然后用结果替换表达式的一部分?感谢您的回复,我已经尝试了几天了:)
    • @thegalah 我也有这种感觉.... :) 除非有一个 非常 好的理由,否则请始终尝试找到一个读取 Prolog 列表的解决方案-向右。然后,您可以使用统一、匹配和尾递归对列表进行自然迭代。是的,但是请查看我对答案的编辑(并投票以便其他人也可以使用它)。
    • @false 是的,当然。在我回答这个问题时,我什至不知道 DCG 是什么。等我睡一觉,我得再看一遍。
    • @Boris:你可以写一个新的答案!进步发生了
    猜你喜欢
    • 2011-03-30
    • 1970-01-01
    • 2015-06-08
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 2011-12-05
    • 2017-05-02
    • 2017-02-10
    相关资源
    最近更新 更多