尝试编写一个给定值和列表的过程,它会删除列表中写入该值的所有内容:
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).
但它不起作用。
答案 0 :(得分:7)
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
。这不是必需的。在最后一个条款之后,没有剩下的选择了(Prolog记住了从左到右,从上到下的选择点),因此切割(删除选项)不会有用,因为你已经在列表的底部选择。
为了说明,请参阅
a :- b; c.
a :- d.
在此,为了证明a
,Prolog将首先尝试b
,然后c
,然后d
(从左到右,然后从上到下)。
除了这个小小的说明,你的问题是你还没有正确理解Prolog的递归。请参阅已经解决此问题的this answer的第一部分。
你的第三个条款错了:
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
,您可以考虑在最后一个子句中添加 guard :
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
统一。< / p>
请注意,我们仍然有一种方法无法使用谓词,+, -, +
就像cTI告诉我们的那样。因此?- delMember(1, R, [2, 3]).
之类的查询会循环显示我的版本。
我希望它有用。
答案 1 :(得分:4)
这不是一个真正的答案,只是对Mog和thanosQR答案的扩展说明,太长而无法发表评论。 这样的答案是令人愉快和有益的,但需要重新考虑削减。考虑:
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].
答案 2 :(得分:3)
让我们做一些改写:因为你想在多个实例化模式中使用谓词“给出一个值和一个列表的过程,它会删除所有出现的值列表“没有定义它在其他情况下应该如何表现。所以,我们可能想要“如果第二个和第三个参数是列表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 = [] .
关于最后一次通话; prolog返回增长的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.
仍然,+ - +不起作用(它处于无限循环中)。但为什么?问题在于条款的顺序:
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
可以包含在包装谓词中,因为它不会影响其他实例化模式。
答案 3 :(得分:0)
这里有一个片段,它也适用于未实例化的第一个和第三个参数:
delMember(X, Y, Z):-
bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).
我将在这里解释这段代码的作用。我的想法是:
第1步是使用setof(X, member(X, Y), L)
完成的,它有两种方式。当参数X
已经实例化时,如果输入参数L
中包含[X]
,X
将是列表Y
,如果X
将失败Y
1}}未包含在X
中。另一方面,如果L
未实例化,则Y
将是输入参数L
的不同元素集。
现在在第2步中,我们回溯了Y
的每个元素,对于此列表的每个成员,我们从输入列表Z
中过滤此元素,从而产生结果。我们在输出列表member(X, Y)
中收集所有这些元素。
请注意,当调用过程时,如果参数X未被实例化,则在回溯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].
的每个成员。
测试用例:
{{1}}