【问题标题】:Postfix to prefix conversion in prolog with Unary Operators of sin cos tan etc在序言中使用 sin cos tan 等一元运算符进行后缀到前缀转换
【发布时间】:2014-02-10 14:23:41
【问题描述】:

我是 Prolog 的新手。我正在努力学习这一点。我正在构建一个后缀来前缀转换谓词。我搜索了很多 google 和 github 等。prolog 递归语法非常混乱。通过大量搜索,我只是found this one link on stackoverflow。我正在解决同样的问题,但指定的解决方案非常令人困惑。我没有想到 append/2 函数的实现,还有一元运算符呢?现在已经超过 3 天了,我一直在努力解决这个问题,只是被搞砸了。任何人请帮助我实现这个逻辑或任何书籍参考或一些链接,以更好地理解同一问题。谢谢

我想这样解决

    post2pre([A,B,C|Rem],Pre) :- Pre=[C,A,B], isop(C).

但问题是如何处理雷姆?如果我在列表中只有一两个项目怎么办 我想像这样解决它

   post2pre([A|[]],Pre) :- Pre=[A].
   post2pre([A,B|[]) :- Pre=[A,B]. 

对于 isOp(),我将它们定义为

   isop(+). isop(-). isop(*). isop(/). isop(sin). isop(cos). isop(exp).

但我不知道如何处理一元运算符?

【问题讨论】:

  • 我已经运行了我发布的链接中指定的代码。但问题是我的大脑仍然无法以 prolog 的方式思考。

标签: data-structures prolog stack logic


【解决方案1】:

好吧,正如我在那个答案中建议的那样, pos2pre/2 这只是一个草图,由学生完成。但这也是一个棘手的解决方案,恕我直言,相当声明。因此,它很容易扩展到处理一元运算符(我将 isop/1 重命名为 is_binary_op/1,以清理代码):

pos2pre(Pos, Pre) :-
    append([A, B, [O]], Pos), is_binary_op(O), A \= [], B \= [],
    pos2pre(A, APre),
    pos2pre(B, BPre),
    !, append([[O], APre, BPre], Pre).
pos2pre(Pos, Pre) :-
    append([A, [O]], Pos), is_unary_op(O), A \= [],
    pos2pre(A, APre),
    !, append([[O], APre], Pre).
pos2pre([P], [P]).

is_binary_op(O) :- memberchk(O,[+,*]).
is_unary_op(O) :- memberchk(O,[sin,tan]).

测试

?- pos2pre([1,2,3,+,*,sin],Pre)   .
Pre = [sin, *, 1, +, 2, 3].

我尝试遵循的另一种方法是使用完全不同的方案,构建一个 中缀 表达式解析器(DCG),然后让后缀/前缀 访问在格式之间转换的树。

edit 这是从 SWI-Prolog 库(列表)中窃取的 append/2 谓词:

append(ListOfLists, List) :-
    must_be(list, ListOfLists),
    append_(ListOfLists, List).

append_([], []).
append_([L|Ls], As) :-
    append(L, Ws, As),
    append_(Ls, Ws).

!符号被命名为cut,它不是一个运算符,而是一个系统谓词(更准确地说,是一个控制谓词)。它总是成功,并且修剪可能在执行点挂起的替代方案,从而承诺到目前为止所做的选择。你应该在网上阅读一些关于这个话题的documentation...

【讨论】:

  • 我认为 gnu 编译器不支持 append/2 函数。并且在整个互联网上,我都在寻找 Append 函数,他们使用 append/3 执行此操作,这两个函数的实现有何不同?你能告诉我!运营商在这里使用?
  • @AbdulRehman,SWI Prolog 有 append/2append/3append/3 也在 GNU prolog 中。如果您在 SWI Prolog 站点上查找这些谓词的描述,您会发现不同之处。简短的回答是append/2 假设第一个参数是一个“列表列表”,它附加到一个列表中(第二个参数)。 append/3 在单个列表级别运行,第三个参数表示附加到第一个参数末尾的第二个参数。请注意,对于这些谓词中的每一个,您都可以给它任何参数,它会为其余的生成解决方案。
【解决方案2】:

这是另一个版本。这个首先从后缀列表构建前缀结构,作为 2 或 3 元素列表(每个一元或二元操作)的嵌套列表结构,然后在最后将嵌套列表展平:

post2pre(Post, Pre) :-
    post2pre(Post, [], Pre).

post2pre([E|T], PreNest, Pre) :-
    (   unary_op(E)
    ->  PreNest = [Term|PNT],
        post2pre(T, [[E,Term]|PNT], Pre)
    ;   binary_op(E)
    ->  PreNest = [Term2,Term1|PNT],
        post2pre(T, [[E,Term1,Term2]|AP], Pre)
    ;   post2pre(T, [E|PreNest], Pre)
    ).
post2pre([], PreNest, Pre) :-
    flatten(PreNest, Pre).

unary_op(X) :-
    memberchk(X, [sin, cos, tan]).

binary_op(X) :-
    memberchk(X, [+, -, /, *]).

在上面的代码中,“原子术语”被隐式定义为任何不是运算符的东西。

根据示例:

| ?- post2pre([1,2,3,+,*,sin],Pre).

Pre = [sin,*,1,+,2,3]

yes

在这种情况下,在flatten 发生之前,“中间”嵌套形式将是:

[sin, [*, 1, [+, 2, 3]]]

上述代码的非 if-then-else 版本可以编写如下。我选择了上面的 if-then-else 构造,因为它避免了对运算符的冗余检查:

post2pre(Post, Pre) :-
    post2pre(Post, [], Pre).

post2pre([E|T], PreNest, Pre) :-
    atomic_term(E),
    post2pre(T, [E|PreNest], Pre).
post2pre([E|T], [Term|PNT], Pre) :-
    unary_op(E)
    post2pre(T, [[E,Term]|PNT], Pre).
post2pre([E|T], [Term2,Term1|PNT], Pre) :-
    binary_op(E)
    post2pre(T, [[E,Term1,Term2]|AP], Pre).
post2pre([], PreNest, Pre) :-
    flatten(PreNest, Pre).

atomic_term(X) :-  % This defines a term explicitly as anything but an operator
    \+ unary_op(X),
    \+ binary_op(X).

unary_op(X) :-
    memberchk(X, [sin, cos, tan]).

binary_op(X) :-
    memberchk(X, [+, -, /, *]).

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    相关资源
    最近更新 更多