想写一个给定值和列表中的程序,它会删除列表中该值的所有的发生写道:
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(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,3,1,2,3,1,2,3]).
(返回true
,当正确的答案是false
)。
我怎样才能使它工作在两种情况呢?
也许我可以检查X is not T
在第三行代码,我想:
delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).
但它不工作。
切的使用
delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).
在这里,你可以看到你用!/0
的谓词的最后一个子句。 这不是必需的。 最后一句话,之后,没有选择离开(序言从左至右,从上到下记得选择点),因此切断(即删除选择),也不会因为你在你的列表的底部是已经做任何有用的选择。
为了说明,请参阅
a :- b; c.
a :- d.
在这里,要证明a
,Prolog的将首先尝试b
,然后c
,然后d
(从左至右,再从上到下)。
顺便说一句,在Prolog的初学者,你应该去尽可能完全避免使用切。 它只是将添加到您的误解,只要你没有得到递归和逻辑编程的基础知识等。
Prolog的递归
那小纸条放在一边,你的问题是你没有正确的理解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
这里。
注意,还是有我们不能用谓词一个办法,这就是, +, -, +
,如CTI告诉我们。 所以查询,如?- delMember(1, R, [2, 3]).
将循环使用我的版本。
我希望它是有用的。
这不是一个真正的答案,只是一个扩展的照会莫古和thanosQR答案,太长,不适合在注释。 这样的答案是愉快的和有益的,但切口去除需要被rethinked。 考虑:
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.
失败在原莫古”的代码,因为在过去的事业的后卫。 这是值得大家注意的是与替换那里的守卫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].
让我们做一点的改写的:既然你想使用一个以上的实例化模式谓语 “给定值和列表中的程序,它删除列表中该值的所有出现”没有定义应该如何表现在其他情况下。 所以,我们可能要像“如果第二个和第三个参数是列表L1,L2和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 = [] .
关于最后通话; 序言返回X,生长导致深度优先算法被用于的列表; 通过使用length/2
,我们可以得到的结果广度优先(_G意味着该变量没有实例化(它可以是任何东西)):
24 ?- length(P,N), del(X, P, Y).
P = [],
N = 0,
Y = [] ;
P = [X],
N = 1,
Y = [] ;
P = [_G548],
N = 1,
Y = [_G548] ;
P = [X, X],
N = 2,
Y = [] ;
P = [X, _G551],
N = 2,
Y = [_G551] ;
P = [_G548, X],
N = 2,
Y = [_G548] ;
P = [_G548, _G551],
N = 2,
Y = [_G548, _G551] ;
P = [X, X, X],
编辑:如@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].
这是因为\==/2
和\=/2
实际上并不强加给变量的限制。 这可以通过切换的第三子句中的规则顺序来解决:
del(_, [], []).
del(X, [X|L1], L2):-
del(X,L1,L2).
del(X, [H|L1], [H|L2]):-
del(X,L1,L2),
X\==H.
4 ?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
Y = [1, 2, 1] ;
false.
然而,这意味着该谓词不再尾递归。 修复,我们可以保持这种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]).
然而,根据以下,尾递归版本实际上是慢:
?-time(forall((between(1,50,N),length(X,N),del2(1,X,[2,3,2,3])),true)).
% 25,600,793 inferences, 5.468 CPU in 5.548 seconds (99% CPU, 4682134 Lips)
true.
?- time(forall((between(1,50,N),length(X,N),del_tr(1,X,[2,3,2,3])),true)).
% 37,346,143 inferences, 6.426 CPU in 6.428 seconds (100% CPU, 5811563 Lips)
true.
尽管如此,+ - +不工作(它属于一个无限循环)。 但为什么? 问题在于条款的顺序:德尔(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(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.
步骤1用做setof(X, member(X, Y), L)
它以两种方式工作。 当参数X
被已经实例化然后L
将或者列表[X]
如果X
被包含在输入参数Y
,如果它将失败X
不包含Y
。 在另一方面,如果X
是未初始化然后L
将是该组输入参数的不同元件的Y
。
现在,在步骤2中,我们原路返回过的每个元素L
,以及用于该列表中的每个成员,我们过滤从输入列表此元素Y
这产生的结果。 我们收集输出列表中的所有元素这Z
。
请注意,当时调用的过程沃斯参数X在非实例,在回溯的member(X, Y)
我们会得到输入列表中的每个成员Y
将被用于过滤。
测试用例:
?- 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].