我想在Prolog中实现谓词noDupl / 2并遇到单例变量的问题

Hod*_*oda 6 list prolog

我的困惑主要在于理解单例变量。

我想noDupl/2在Prolog中实现谓词。该谓词可用于识别列表中仅出现一次的数字,即没有重复的数字。的第一个参数noDupl是要分析的列表。第二个参数是不重复的数字列表,如下所述。例如,对于列表[2, 0, 3, 2, 1][0, 3, 1]将计算结果(因为2重复)。在我的实现中,我使用了预定义的成员谓词,并使用了一个称为helper的辅助谓词。

我将用伪代码解释我的逻辑,以便您可以帮助我确定我哪里出错了。

  1. 首先,如果第一个元素不是列表其余部分的成员,则将第一个元素添加到新结果List中(以其开头)。
  2. 如果第一个元素是的成员T,请在列表的其余部分,第一个元素H和新列表上调用helper方法。
  3. 助手方法,如果H在尾部找到,则返回不带的列表H,即Tail

    noDupl([],[]).
    noDupl([H|T],L) :-
       \+ member(H,T),
        noDupl(T,[H|T]).
    noDupl([H|T],L) :-
       member(H,T),
       helper(T,H,L).
    
    helper([],N,[]).
    helper([H|T],H,T). %found place of duplicate & return list without it
    helper([H|T],N,L) :-
       helper(T,N,[H|T1]).%still couldn't locate the place, so add H to the new List as it's not a duplicate
    
    Run Code Online (Sandbox Code Playgroud)

在编写代码时,在选择新变量或使用谓词参数中定义的变量(特别是关于自由变量)时总是遇到麻烦。谢谢。

rep*_*eat 5

关于单例变量的警告不是实际问题。

单例变量是在某些Prolog子句(事实或规则)中出现一次的逻辑变量。如果这些变量的名称类似于非单变量,即它们的名称不以开头,则Prolog会警告您_

该约定有助于避免打字错误,因为打字错误不会引起语法错误,但改变含义。


让我们为您的问题建立一个规范的解决方案。

首先,忘记CamelCase并选择一个合适的谓词名称来反映当前问题的关系性质:怎么样list_uniques/2

然后,记录您希望谓词给出一个答案,多个答案或根本没有答案的案例。怎么样?不只是文本,而是查询

从最一般的查询开始:

?- list_uniques(Xs, Ys).
Run Code Online (Sandbox Code Playgroud)

添加一些地面查询:

?- list_uniques([], []).
?- list_uniques([1,2,2,1,3,4], [3,4]).
?- list_uniques([a,b,b,a], []).
Run Code Online (Sandbox Code Playgroud)

并添加包含变量的查询:

?- list_uniques([n,i,x,o,n], Xs).
?- list_uniques([i,s,p,y,i,s,p,y], Xs).

?- list_uniques([A,B], [X,Y]).
?- list_uniques([A,B,C], [D,E]).
?- list_uniques([A,B,C,D], [X]).
Run Code Online (Sandbox Code Playgroud)

现在让我们编写一些代码!基于library(reif)写:

:-use_module(library(reif))。

list_uniques(Xs,Ys):-
   list_past_uniques(Xs,[],Ys)。

list_past_uniques([],_,[])。%辅助谓词
list_past_uniques([X | Xs],Xs0,Ys):-
   if _((memberd_t(X,Xs); memberd_t(X,Xs0)),
       Ys = Ys0,
       Ys = [X | Ys0]),
   list_past_uniques(Xs,[X | Xs0],Ys0)。

这是怎么回事?

  • list_uniques/2 基于帮助谓词 list_past_uniques/3

  • 在任何时候,请list_past_uniques/3跟踪:

    • 前面的所有项目(Xs
    • 所有项目都在“后面”(Xs0)原始列表中的某些项目X
  • 如果X任一列表的成员,则Ys跳过X—它不是唯一的

  • 否则,它X 唯一的,并且出现在Ys(作为其列表头)。

让我们使用SWI-Prolog 8.0.0运行上面的一些查询:

?- list_uniques(Xs, Ys).
   Xs = [], Ys = []
;  Xs = [_A], Ys = [_A]
;  Xs = [_A,_A], Ys = []
;  Xs = [_A,_A,_A], Ys = []
...

?- list_uniques([], []).
true.
?- list_uniques([1,2,2,1,3,4], [3,4]).
true.
?- list_uniques([a,b,b,a], []).
true.

?- list_uniques([1,2,2,1,3,4], Xs).
Xs = [3,4].
?- list_uniques([n,i,x,o,n], Xs).
Xs = [i,x,o].
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
Xs = [].

?- list_uniques([A,B], [X,Y]).
A = X, B = Y, dif(Y,X).
?- list_uniques([A,B,C], [D,E]).
false.
?- list_uniques([A,B,C,D], [X]).
   A = B, B = C, D = X, dif(X,C)
;  A = B, B = D, C = X, dif(X,D)
;  A = C, C = D, B = X, dif(D,X)
;  A = X, B = C, C = D, dif(D,X)
;  false.
Run Code Online (Sandbox Code Playgroud)

  • 出色的格式和给出答案的方式。 (2认同)

Use*_*213 -1

您在第一个谓词 中已经存在问题noDupl/2

第一条noDupl([], []).看起来不错。第二条是错误的。

noDupl([H|T],L):-
    \+member(H,T),
    noDupl(T,[H|T]).
Run Code Online (Sandbox Code Playgroud)

这到底意味着什么,我留给你作为练习。但是,如果您想添加H到结果中,您可以这样写:

noDupl([H|T], [H|L]) :-
    \+ member(H, T),
    noDupl(T, L).
Run Code Online (Sandbox Code Playgroud)

请仔细看一下并尝试理解。H通过将结果(头部中的第二个参数)统一到一个列表中,将其添加到结果中,该列表作为H头部,变量L作为尾部。您定义中的单例变量L是单例,因为您的定义中有一个错误,即您对它什么都不做。

最后一个子句有不同类型的问题。您尝试从该元素中清除列表的其余部分,但您永远不会返回到删除所有重复项的原始任务。它可以这样修复:

noDupl([H|T], L) :-
    member(H, T),
    helper(T, H, T0),
    noDupl(T0, L).
Run Code Online (Sandbox Code Playgroud)

helper/3从重复项中清除原始列表的其余部分,将结果与 统一T0,然后使用此干净列表继续删除重复项。

现在轮到你的帮手了。第一个子句看起来不错,但有一个单例变量。这是一个有效的情况,您不想对此参数执行任何操作,因此您“声明”它未使用,例如如下所示:

helper([], _, []).
Run Code Online (Sandbox Code Playgroud)

第二个子句是有问题的,因为它删除了单个事件。如果您致电,会发生什么情况:

?- helper([1,2,3,2], 2, L).
Run Code Online (Sandbox Code Playgroud)

最后一条也有问题。仅仅因为您对两个变量使用不同的名称,这并不会使它们不同。要修复这两个子句,您可以执行以下操作:

helper([H|T], H, L) :-
    helper(T, H, L).
helper([H|T], X, [H|L]) :-
    dif(H, X),
    helper(T, X, L).
Run Code Online (Sandbox Code Playgroud)

noDupl/2这些是最小的更正,当 的第一个参数成立时,它们将为您提供答案。noDupl/2您可以通过重命名noDupl_ground/2并定义为来执行此检查noDupl/2

noDupl(L, R) :-
    must_be(ground, L),
    noDupl_ground(L, R).
Run Code Online (Sandbox Code Playgroud)

尝试看看使用当前的简单实现对于不同查询会得到什么,并询问您是否还有其他问题。它仍然充满问题,但这实际上取决于您将如何使用它以及您想要从答案中得到什么。

  • 难道我们不应该给出更好的答案吗? (2认同)