【问题标题】:Prolog unification and optimizationProlog统一和优化
【发布时间】:2018-04-20 14:39:21
【问题描述】:

我想编写合并两个列表的合并函数,我可以通过编写两个(合并)谓词来做到这一点,一个元素 H1 = H2 但我想用 if-the- 编写其他条件,但随后统一失败,我不知道该怎么做。这是我的代码:

merge([H1|T1],[H2|T2],List):-
  (
   H1 =< H2 ->
        NewList = [H1|List],
        merge(T1,[H2|T2],NewList)
  ;
        NewList = [H2|List],
        merge([H1|T1],T2,NewList)
  ).

merge([],L,L).
merge(L,[],L).

我知道问题出在哪里,如果我写了两个合并谓词,我可以避免它,但我不知道怎么做,我被卡住了:(

模式匹配在这里失败:

【问题讨论】:

  • 第一个子句必须是最后一个子句!

标签: list merge prolog


【解决方案1】:

其实你已经很接近了。看看这个踪迹:

?- trace, merge([1,3,5,6], [2,4,7,8], X).
   Call: (9) merge([1, 3, 5, 6], [2, 4, 7, 8], _6684) ? creep
   Call: (10) 1=<2 ? creep
   Exit: (10) 1=<2 ? creep
   Call: (10) _7050=[1|_6684] ? creep
   Exit: (10) [1|_6684]=[1|_6684] ? creep
   Call: (10) merge([3, 5, 6], [2, 4, 7, 8], [1|_6684]) ? creep
   Call: (11) 3=<2 ? creep
   Fail: (11) 3=<2 ? creep
   Redo: (10) merge([3, 5, 6], [2, 4, 7, 8], [1|_6684]) ? creep
   Call: (11) _7062=[2, 1|_6684] ? creep
   Exit: (11) [2, 1|_6684]=[2, 1|_6684] ? creep
   Call: (11) merge([3, 5, 6], [4, 7, 8], [2, 1|_6684]) ? creep
   Call: (12) 3=<4 ? creep
   Exit: (12) 3=<4 ? creep
   Call: (12) _7074=[3, 2, 1|_6684] ? creep
   Exit: (12) [3, 2, 1|_6684]=[3, 2, 1|_6684] ? creep
   Call: (12) merge([5, 6], [4, 7, 8], [3, 2, 1|_6684]) ? creep
   Call: (13) 5=<4 ? creep
   Fail: (13) 5=<4 ? creep
   Redo: (12) merge([5, 6], [4, 7, 8], [3, 2, 1|_6684]) ? creep
   Call: (13) _7086=[4, 3, 2, 1|_6684] ? creep
   Exit: (13) [4, 3, 2, 1|_6684]=[4, 3, 2, 1|_6684] ? creep
   Call: (13) merge([5, 6], [7, 8], [4, 3, 2, 1|_6684]) ? creep
   Call: (14) 5=<7 ? creep
   Exit: (14) 5=<7 ? creep
   Call: (14) _7098=[5, 4, 3, 2, 1|_6684] ? creep
   Exit: (14) [5, 4, 3, 2, 1|_6684]=[5, 4, 3, 2, 1|_6684] ? creep
   Call: (14) merge([6], [7, 8], [5, 4, 3, 2, 1|_6684]) ? creep
   Call: (15) 6=<7 ? creep
   Exit: (15) 6=<7 ? creep
   Call: (15) _7110=[6, 5, 4, 3, 2, 1|_6684] ? creep
   Exit: (15) [6, 5, 4, 3, 2, 1|_6684]=[6, 5, 4, 3, 2, 1|_6684] ? creep

在递归深度的这一刻,你几乎有了解决方案。然后它开始出错:

   Call: (15) merge([], [7, 8], [6, 5, 4, 3, 2, 1|_6684]) ? creep
   Fail: (15) merge([], [7, 8], [6, 5, 4, 3, 2, 1|_6684]) ? creep
   Redo: (14) merge([6], [7, 8], [5, 4, 3, 2, 1|_6684]) ? creep
   Fail: (14) merge([6], [7, 8], [5, 4, 3, 2, 1|_6684]) ? creep
   Redo: (13) merge([5, 6], [7, 8], [4, 3, 2, 1|_6684]) ? creep
   Fail: (13) merge([5, 6], [7, 8], [4, 3, 2, 1|_6684]) ? creep
   Redo: (12) merge([5, 6], [4, 7, 8], [3, 2, 1|_6684]) ? creep
   Fail: (12) merge([5, 6], [4, 7, 8], [3, 2, 1|_6684]) ? creep
   Redo: (11) merge([3, 5, 6], [4, 7, 8], [2, 1|_6684]) ? creep
   Fail: (11) merge([3, 5, 6], [4, 7, 8], [2, 1|_6684]) ? creep
   Redo: (10) merge([3, 5, 6], [2, 4, 7, 8], [1|_6684]) ? creep
   Fail: (10) merge([3, 5, 6], [2, 4, 7, 8], [1|_6684]) ? creep
   Redo: (9) merge([1, 3, 5, 6], [2, 4, 7, 8], _6684) ? creep
   Fail: (9) merge([1, 3, 5, 6], [2, 4, 7, 8], _6684) ? creep

那么您的代码有什么问题?主要有两件事:您正在构建反转的结果(并不少见)并且您没有很好的方法将其传回。安排一个结果变量当然是处理这些问题的一种方法,类似于 Luai 的想法:

merge(X,Y,Z) :- merge(X, Y, [], Z).

merge([H1|T1],[H2|T2],List,NewList):-
  (
   H1 =< H2 ->
        merge(T1,[H2|T2],[H1|List],NewList)
  ;
        merge([H1|T1],T2,[H2|List],NewList)
  ).

merge([],R,LRev,Res) :- reverse(LRev, L), append(L,R,Res).
merge(R,[],LRev,Res) :- reverse(LRev, L), append(L,R,Res).

或者你将不得不想出另一种方法来做到这一点,例如传递列表的尾部,如下所示:

merge2([H1|T1], [H2|T2], MT) :-
    (H1 =< H2 ->
        MT=[H1|MT2],
        merge2(T1, [H2|T2], MT2)
    ;
        MT=[H2|MT2],
        merge2([H1|T1], T2, MT2)
    ).
merge2([], T2, T2).
merge2(T2, [], T2).

我个人更喜欢这种方法。

【讨论】:

    【解决方案2】:

    你对递归的看法是错误的。第三个参数是合并的结果

    你在这里使用:

    merge([H1|T1],[H2|T2],List):-
      (
       H1 =< H2 ->
            NewList = [H1|List],
            merge(T1,[H2|T2],NewList)
      ;
            NewList = [H2|List],
            merge([H1|T1],T2,NewList)
      ).
    
    merge([],L,L).
    merge(L,[],L).

    换句话说,您在递归调用的输出之前,实际上您希望递归输出以H1 和/或@987654323 开始 @ 然后从结果中弹出该元素并将剩余部分(列表的 tail)作为结果传递。

    所以一个快速的解决办法是扭转这种情况:

    merge([H1|T1],[H2|T2],List):-
      (
       H1 =< H2 ->
            List = [H1|NewList],
            merge(T1,[H2|T2],NewList)
      ;
            List = [H2|NewList],
            merge([H1|T1],T2,NewList)
      ).
    
    merge([],L,L).
    merge(L,[],L).

    现在我们注意到有一些重复的代码,所以我们可以将其重写为:

    merge([H1|T1],[H2|T2],[Min|NewList]):-
      (
       H1 =< H2 ->
            Min = H1,
            merge(T1,[H2|T2],NewList)
      ;
            Min = H2,
            merge([H1|T1],T2,NewList)
      ).
    
    merge([],L,L).
    merge(L,[],L).

    但就我个人而言,我发现两个子句的解决方案更优雅:

    merge([H1|T1],[H2|T2],[H1|T3]) :-
       H1 =< H2,
       merge(T1,[H2|T2],T3).
    
    merge([H1|T1],[H2|T2],[H2|T3]) :-
       H1 > H2,
       merge([H1|T1],T2,T3).
    
    merge([],L,L).
    merge(L,[],L).

    【讨论】:

    • 我也更喜欢双子句解决方案,但 OP 说“我可以通过编写两个(合并)谓词来做到这一点......但是我想用 if-the-else 条件编写”
    • @DanielLyons:我注意到了。但即使他对这个答案使用双子句方法,它当然也行不通。答案有两个方面,首先提供解决方案,然后提供一些(我认为)有用的建议。
    • 我认为这是非常有用的建议。
    • 还有一个选择是这样编码:merge([A|B], [C|D], [E|F]):- ( A =&lt; C -&gt; E=A, L1=B, L2=[C|D] ; E=C, L1=[A|B], L2=D ), merge(L1, L2, F). 这样你就不会在你的最后一个代码中出现我刚刚注意到的错误。 :)
    • A =&lt; B -&gt; ...?
    【解决方案3】:

    应该有更好的方法,但这也是一种选择。

    merge(List1,List2,Result):-
      merge(List1,List2,[],Result).
    
    merge([],L,X,Result):-
       append(X,L,Result).
    merge(L,[],X,Result):-
        append(X,L,Result).
    
    merge([H1|T1],[H2|T2],Acc,Result):-
          (
           H1 =< H2 ->
                apend(Acc,H1,NewList),
                merge(T1,[H2|T2],NewList,Result)
          ;
               apend(Acc,H2,NewList),
               merge([H1|T1],T2,NewList,Result)
          ).
    
    %i think prolog implementation of append/3 does not work with empty list so i implemented here.
    apend([],X,[X]).
    
    apend([H|T],X,[H|T2]):-
      apend(T,X,T2).
    

    【讨论】:

    • 请不要使用apend/3(或内置append/3),除非你必须:它在O(n)中工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多