我的困惑主要在于理解单例变量。
我想noDupl/2在Prolog中实现谓词。该谓词可用于识别列表中仅出现一次的数字,即没有重复的数字。的第一个参数noDupl是要分析的列表。第二个参数是不重复的数字列表,如下所述。例如,对于列表[2, 0, 3, 2, 1],[0, 3, 1]将计算结果(因为2重复)。在我的实现中,我使用了预定义的成员谓词,并使用了一个称为helper的辅助谓词。
我将用伪代码解释我的逻辑,以便您可以帮助我确定我哪里出错了。
T,请在列表的其余部分,第一个元素H和新列表上调用helper方法。助手方法,如果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)在编写代码时,在选择新变量或使用谓词参数中定义的变量(特别是关于自由变量)时总是遇到麻烦。谢谢。
关于单例变量的警告不是实际问题。
单例变量是在某些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)
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)
尝试看看使用当前的简单实现对于不同查询会得到什么,并询问您是否还有其他问题。它仍然充满问题,但这实际上取决于您将如何使用它以及您想要从答案中得到什么。