【问题标题】:Why does my Prolog predicate invert/2 not work?为什么我的 Prolog 谓词 invert/2 不起作用?
【发布时间】:2016-01-25 10:20:13
【问题描述】:

我是 Prolog 的新手,作为练习,我想做一个列表倒置谓词。它使用了我之前创建的add_tail 谓词——有些部分可能是多余的,但我不在乎:

add_tail(A, [], A) :-
   !.
add_tail([A|[]], H, [A,H]) :-
   !.
add_tail([A|B], H, [A|C]) :- 
   add_tail(B,H,C).

它和内置谓词append/3一样工作:

?- add_tail([a,b,c], d, A).
A = [a, b, c, d].

?- append([a,b,c], [d], A).
A = [a, b, c, d].

当我在invert 谓词中使用append 时,它可以正常工作,但如果我使用add_tail,它会失败:

invert([], []).
invert([A|B], C) :- 
   invert(B, D),
   append(D, [A], C).

invert2([], []).
invert2([A|B], C) :- 
   invert2(B, D),
   add_tail(D, A, C).

?- invert([a,b,c,d], A).
A = [d, c, b, a].

?- invert2([a,b,c,d], A).
false.                          % expected answer A = [d,c,b,a], like above

我的错误到底是什么?谢谢!

【问题讨论】:

    标签: list prolog


    【解决方案1】:

    add_tail/3 的实现确实完全按照您期望的方式运行。 考虑:

    ?- 附加([],[d],Xs)。 Xs = [d]。 ?- add_tail([], d, Xs)。 错误

    这很糟糕......但它变得更糟了!您提供的代码还有更多问题:

    • 通过使用(!)/0,您不必要地限制了谓词的多功能性。

    • 即使[A|[]] 可能正确,它也会混淆您的代码。请改用[A]

    • add_tail 对于在多个方向上起作用的谓词来说是一个坏名字。

    • 变量名也可以更好!为什么不使用更具描述性的名称,例如As

      再看看你在add_tail/3的最后一个子句中使用的变量!

      add_tail([A|B], H, [A|C]) :- 添加尾(B,H,C)。

      考虑改进的变量名称:

      add_tail([A|As], E, [A|Xs]) :- add_tail(As, E, Xs)。

    我建议这样重新开始:

    列表项附加([],X,[X])。 list_item_appended([E|Es], X, [E|Xs]) :- 列表项附加(Es,X,Xs)。

    让我们把list_item_appended/3用在list_reverted/2中!

    list_reverted([], [])。 list_reverted([E|Es], Xs) :- list_reverted(Es, Fs), 列表项附加(Fs,E,Xs)。

    示例查询:

    ?- list_reverted([a,b,c,d], Xs).
    Xs = [d, c, b, a].
    

    【讨论】:

      【解决方案2】:

      很难确定您的确切错误,但是add_tail/3 的前两个子句,即带有删减的子句,是错误的(除非我误解了谓词应该做什么) .这个名字已经有点误导了,你应该注意你有多余的代码。

      list_back([], B, [B]).
      list_back([X|Xs], B, [X|Ys]) :-
          list_back(Xs, B, Ys).
      

      这是在invert/2 的定义中替换您的add_tail/3。但正如您可能知道的那样,这不是一个非常聪明的反转列表的方法。如何做到这一点的教科书示例:

      list_rev(L, R) :-
          list_rev_1(L, [], R).
      
      list_rev_1([], R, R).
      list_rev_1([X|Xs], R0, R) :-
          list_rev_1(Xs, [X|R0], R).
      

      【讨论】:

        【解决方案3】:

        首先尝试最一般的查询,看看在最一般的情况下存在哪些解决方案:

        ?- add_tail(X, Y, Z). 
        

        给出唯一的答案:

        X = Z,
        Y = []
        

        这可能不是您打算在此处定义的关系。

        提示:!/0 通常会破坏代码的所有逻辑属性,包括在所有方向上使用谓词的能力。

        【讨论】:

        • s(X) 将重点转移到更一般的用途上。
        【解决方案4】:

        add_tail/3 的第一个子句有一个 list 作为第二个参数,因此它永远不会应用于您的测试用例。然后我们剩下2个子句(简化)

        add_tail([A],H,[A,H]):-!.
        add_tail([A|B],H,[A|C]) :- add_tail(B,H,C).
        

        你可以看到我们错过了空列表的匹配子句作为第一个参数。当然, append/3 代替 这样的匹配。

        【讨论】:

          【解决方案5】:

          根据之前对“@mat”的回答,问题是前两行有残留

          你的谓词add_tail 不像append 因为

          append 我明白了

          | ?- append(X,Y,Z).
          Z = Y,
          X = [] ? ;
          X = [_A],
          Z = [_A|Y] ? ;
          X = [_A,_B],
          Z = [_A,_B|Y] ? ;
          X = [_A,_B,_C],
          Z = [_A,_B,_C|Y] ? ;
          X = [_A,_B,_C,_D],
          Z = [_A,_B,_C,_D|Y] ? ;
          X = [_A,_B,_C,_D,_E],
          Z = [_A,_B,_C,_D,_E|Y] ? ;y
          

          不幸的是,你的add_tail 我得到了这个结果

          | ?- add_tail(X,Y,Z).
          Z = X,
          Y = [] ? ;
          X = [_A],
          Z = [_A|Y] ? ;
          X = [_A|_B],
          Y = [],
          Z = [_A|_B] ? ;
          X = [_A,_B],
          Z = [_A,_B|Y] ? ;
          X = [_A,_B|_C],
          Y = [],
          Z = [_A,_B|_C] ? 
          X = [_A,_B,_C],
          Z = [_A,_B,_C|Y] ? y
          yes
          

          在您的add_tail 代码中进行简单修改后,我得到了您的预期结果

          代码

          %   add_tail(A,[],A):-! . comment
          
          add_tail([],H,H) :-!.
          add_tail([A|B],H,[A|C]) :- add_tail(B,H,C).  
          

          测试add_tail

          | ?- add_tail(X,Y,Z).
          
              Z = Y,
              X = [] ? ;
              X = [_A],
              Z = [_A|Y] ? ;
              X = [_A,_B],
              Z = [_A,_B|Y] ? ;
              X = [_A,_B,_C],
              Z = [_A,_B,_C|Y] ? ;
              X = [_A,_B,_C,_D],
              Z = [_A,_B,_C,_D|Y] ? ;
              X = [_A,_B,_C,_D,_E],
              Z = [_A,_B,_C,_D,_E|Y] ? y
              yes
          

          终于

          我测试你的invert谓词没有修改

          | ?- invert([_A,_B,_C],L).
          L = [_C,_B,_A] ? ;
          no
          

          我希望这篇文章能帮助你解释谓词是如何在里面完成的

          享受

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-04-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多