我需要一个谓词来确定元素是否在列表中。我尝试使用member / 2,但是我注意到了一种奇怪的行为。
当我调用member(1, [1, 2, 3]).
之类的东西时,SWI-prolog输出{{1}}并等待我按Enter键,然后执行终止。当元素不在列表中时,不会发生这种情况。
我有两个问题:
答案 0 :(得分:1)
- 为什么会这样?
member/2
的定义允许成功多次,即使找到了完全相同的解决方案。在您的列表中,所有元素都不相同。但是请考虑在末尾再出现两次的情况:
?- member(1,[1,2,3,1,1]).
true ;
true ;
true.
- 将立即返回的member / 2的代码是什么?
通常称为memberd/2
。
?- memberd(1,[1,2,3,1,1]).
true.
?- memberd(X,[1,2,3,1,1]).
X = 1 ;
X = 2 ;
X = 3 ;
false.
差异很容易掌握。 member/2
本质上定义为:
member(X, [X|_Xs]).
member(E, [_X|Xs]) :-
member(E, Xs).
请注意,这两个子句不是互斥的!这就是即使已找到解决方案也要搜索替代解决方案的原因。在规则中,E
和_X
可能是相同的。
通过确保E
和X
的规则不同,我们得到memberd/2
的第一个定义:
memberd(X, [X|_Xs]).
memberd(E, [X|Xs]) :-
dif(E, X),
memberd(E, Xs).
由于此dif/2
,仅当当前元素与E
不同时,才考虑使用该规则。但是,这里缺少的是,Prolog在断定它们是不相交的之前仍然必须考虑两者。
完整定义包含一些避免不必要选择的技术。
memberd(X, [E|Es]) :-
if_(X = E, true, memberd(X, Es)).
使用library(reif)
对于
SICStus和
SWI扩展为:
memberd(X, [E|Es]) :-
( X\=E
-> memberd(X, Es)
; X==E
-> true
; X=E,
true
; dif(X, E),
memberd(X, Es)
).
答案 1 :(得分:1)
这是第二个问题的替代答案。
- 将立即返回的member / 2的代码是什么?
如果您只想检查X
是否是Es
的成员并且两者都为基础,则可以使用
memberchk(X, Es).
memberchk/2
是built-in in SWI-Prolog,但如果不是,则可以将其定义为
memberchk(X, Es) :-
member(X, Es),
!.
!
调用后的剪切(member/2
)告诉Prolog忘记了该调用可能仍然需要尝试的其他选择。
您的示例确定性(立即)成功:
?- memberchk(1, [1, 2, 3]).
true.
但是请注意,memberchk/2
与member/2
完全不同。例如:
?- findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].
?- findall(X, memberchk(X, [1, 2, 3]), Xs).
Xs = [1].