Prolog 从列表中删除元素的所有引用
尝试编写给定值和列表的过程时,会删除所写入列表中该值出现的所有内容:Prolog 从列表中删除元素的所有引用,prolog,prolog-dif,Prolog,Prolog Dif,尝试编写给定值和列表的过程时,会删除所写入列表中该值出现的所有内容: delMember(X, [], []) :- !. delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y). delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y). 由于cut无法正确回答以下问题: delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2,
delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).
由于cut
无法正确回答以下问题:
delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).
delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
如果我删除削减:
delMember(X, [], []).
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).
它在以下查询中失败:
delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).
delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
(当正确答案为false
时,返回true
)
如何使其在这两种情况下都有效
也许我可以检查第三行代码中的X不是T
,我尝试了:
delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).
但它不起作用。使用切割
在这里,您可以看到您使用了/0
在谓词的最后一个子句中。没必要。在最后一个子句之后,没有选择了(Prolog从左到右、从上到下记住选择点),因此剪切(删除选择)不会做任何有用的事情,因为您已经在选择列表的底部
要进行说明,请参见
a :- b; c.
a :- d.
在这里,为了证明a
,Prolog将首先尝试b
,然后是c
,然后是d
(从左到右,从上到下)
顺便说一句,作为Prolog的初学者,您应该尽量避免使用cut。它只会增加你的误解,只要你没有得到递归和其他基本的逻辑编程
序言递归
撇开这点不谈,你的问题是你还没有正确理解Prolog递归。请看第一部分已经解决了这个问题
你的第三条错了:
delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).
应改为:
delMember(X, [T|Xs], [T|Y]) :- delMember(X, Xs, Y).
嗯,这不是真的错,只是真的不太理想。它不是尾部递归的,并且使用append/3
,这将把线性谓词变成二次谓词。另外,正如您所注意到的,因为它不是尾部递归的,所以在某些情况下,终止更难获得
然后,取消使用切割/0 ,可以考虑添加一个保护到最后一个子句:
delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
delMember(X, Xs, Y).
delMember(X, [T|Xs], [T|Y]) :-
dif(X, T),
delMember(X, Xs, Y).
守卫,dif(X,T)
,指定如果我们在第三种情况下,我们不能同时在第二种情况下:X
不能与T
统一
注意,还有一种方法我们不能使用谓词,这就是,+,-,+
,正如我们所说的。所以像?-delMember(1,R,[2,3])这样的查询将与我的版本循环
我希望它是有用的。让我们重新表述一下:因为您希望在多个实例化模式中使用谓词,“一个给定值和列表的过程,它会删除列表中所有出现的值”并没有定义它在其他情况下的行为。因此,我们可能希望类似“如果第二个和第三个参数是列表L1,则谓词为true,如果忽略第一个参数的所有出现,则L1与L2是同一列表”
现在,有两种方法可以编写具有多个可能实例化的谓词;您可以使用元逻辑谓词,例如var/1
和ground/1
,并为每一个谓词编写代码(这可能允许您编写针对特定实例化进行优化的代码),或者编写逻辑上定义属性的代码(这可能更具挑战性)
在这种情况下,我们可以这样做:
del(_, [], []).
del(X, [X|L1], L2):-
del(X,L1,L2).
del(X, [H|L1], [H|L2]):-
X\==H,
del(X,L1,L2).
具有以下行为:
19 ?- del(1, [1,2,3], X).
X = [2, 3] ;
false.
1,2,3,
20 ?- del(1, [1,2,3], [2,3]).
true ;
false.
21 ?- del(X, [1,2,3], [2,3]).
X = 1 ;
false.
22 ?- del(X, [1,2,3], Y).
X = 1,
Y = [2, 3] ;
X = 2,
Y = [1, 3] ;
X = 3,
Y = [1, 2] ;
Y = [1, 2, 3] ;
false.
23 ?- del(X, P, Y).
P = Y, Y = [] ;
P = [X],
Y = [] ;
P = [X, X],
Y = [] ;
P = [X, X, X],
Y = [] ;
P = [X, X, X, X],
Y = [] ;
P = [X, X, X, X, X],
Y = [] ;
P = [X, X, X, X, X, X],
Y = [] .
关于最后一次通话;prolog返回一个X列表,该列表因使用深度优先算法而增长;通过使用length/2
我们可以得到宽度优先的结果(_G意味着变量没有实例化(它可以是任何东西)):
编辑:正如@chac所指出的,如果第一个列表(至少)有一个重复元素,则上述谓词的行为不正确:
?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ; <----- wrong
Y = [1, 2, 1].
然而,这意味着谓词不再是尾部递归的。为了解决这个问题,我们可以保留一个X不应该是的值的列表:
del(X,L1,L2):-
del(X,L1,L2,[]).
del(X, [], [], NotX):-
\+ member(X,NotX).
del(X, [X|L1], L2, NotX):-
del(X,L1,L2,NotX).
del(X, [H|L1], [H|L2], NotX):-
X\==H, % <--- optional; stops the execution earlier (saving time)
del(X,L1,L2,[H|NotX]).
尽管如此,+-+仍然不起作用(它落在一个无限循环中)。但是为什么呢?问题在于条款的顺序:
del(1,L1,[2])将首先应用将X“添加”到L1头部的规则,然后永远应用相同的规则。
可以(再次)使用length/2
:
?- length(X,2), del(1,X,[2]).
X = [1, 2] ;
X = [2, 1] ;
false.
或者,我们可以更改条款的顺序:
del(_, [], []).
del(X, [H|L1], [H|L2]):-
X\==H,
del(X,L1,L2),
X\==H.
del(X, [X|L1], L2):-
del(X,L1,L2).
但是length/2
可能再次有用,因为没有它,prolog会执行深度优先搜索:
?- del(1,X,[2]).
X = [2] ;
X = [2, 1] ;
X = [2, 1, 1] ;
X = [2, 1, 1, 1] ;
X = [2, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1, 1]
当然,length/2
可以合并到包装谓词中,因为它不会影响其他实例化模式。这不是一个真正的答案,只是对和答案的扩展注释,太长了,无法放入注释中。
这些答案是令人愉快和有启发性的,但割伤的去除需要重新考虑。考虑:
delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
delMember(X, Xs, Y), !.
delMember(X, [T|Xs], [T|Y]) :-
delMember(X, Xs, Y).
这个定义允许
?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.
这在原始Mog代码中失败,因为最后一个原因是防护。值得注意的是,用X\==T
(这将测试限制为匹配的实例化状态)替换那里的保护也解决了这个查询,正如thanosQR所指出的
但这些片段都不能解决一般情况:
?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;
Y = [1, 2, 1].
下面是一个代码片段,它也适用于未实例化的第一个和第三个参数:
delMember(X, Y, Z):-
bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).
我将在这里解释这段代码的作用。其想法是:
构建一个输入列表Y的不同成员列表,该列表与输入成员X统一
然后,对于构建在1)上的列表中的每个X,从输入列表中丢弃该元素,以获得不包含成员X的输出列表Z
步骤1是用setof(X,member(X,Y),L)
完成的,它有两种工作方式。当参数X
已实例化时,如果X
包含在输入参数Y
中,则L
将是列表[X]
,如果X
未包含在Y
中,则该列表将失败。另一方面,如果X
未实例化,则L
将是输入参数Y
的不同元素集
现在,在步骤2中,我们回溯L
的每个元素,以及该元素的每个成员
?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;
Y = [1, 2, 1].
delMember(X, Y, Z):-
bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).
?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
false.
?- delMember(Y, [1,2,3,1,2,3], X).
Y = 1,
X = [2, 3, 2, 3] ;
Y = 2,
X = [1, 3, 1, 3] ;
Y = 3,
X = [1, 2, 1, 2].
?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.
?- delMember(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1].