为什么我的Prolog谓词反转/ 2不起作用?

时间:2016-01-25 10:20:13

标签: list prolog

我是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].

当我在append谓词中使用invert时,它运行正常,但如果我使用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

我的错误究竟是什么?谢谢!

5 个答案:

答案 0 :(得分:2)

很难确定你的确切的错误,但是add_tail/3的前两个条款(带有剪切的条款)是错误的(除非我误解了谓词应该是什么做)。这个名称有点误导,你应该关心你有多余的代码。

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

这是您add_tail/3定义中invert/2的替代品。但是你可能知道,这不是一个非常聪明的方法来反转列表。如何做的教科书示例:

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).

答案 1 :(得分:2)

add_tail/3的实现的行为与您期望的完全相同。 考虑:

?- append([], [d], Xs).
Xs = [d].

?- add_tail([], d, Xs).
false.

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

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

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

  • add_tail是一个在多个方向上工作的谓词的错误名称。

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

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

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

    考虑改进的变量名称:

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

我建议从这样开始:

list_item_appended([], X, [X]).
list_item_appended([E|Es], X, [E|Xs]) :-
   list_item_appended(Es, X, Xs).

让我们在list_item_appended/3中使用list_reverted/2

list_reverted([], []).
list_reverted([E|Es], Xs) :- 
   list_reverted(Es, Fs),
   list_item_appended(Fs, E, Xs).

示例查询:

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

答案 2 :(得分:1)

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

?- add_tail(X, Y, Z). 

得出单一答案:

X = Z,
Y = []

这可能不是你打算在这里定义的关系。

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

答案 3 :(得分:0)

基于之前的答案" @ mat"问题是前两行中的残留

您的谓词add_tailappend不同,因为

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

,不幸的是,我{ur了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

finaly

我在没有修改的情况下测试你的invert谓词

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

我希望这篇文章可以帮助你解释谓词内部是如何完成的。

享受

答案 4 :(得分:0)

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

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

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