为什么member / 2不立即返回

时间:2019-04-14 19:55:50

标签: prolog

我需要一个谓词来确定元素是否在列表中。我尝试使用member / 2,但是我注意到了一种奇怪的行为。

当我调用member(1, [1, 2, 3]).之类的东西时,SWI-prolog输出{{​​1}}并等待我按Enter键,然后执行终止。当元素不在列表中时,不会发生这种情况。

我有两个问题:

  1. 为什么会这样?
  2. 将立即返回的member / 2的代码是什么?

2 个答案:

答案 0 :(得分:1)

  
      
  1. 为什么会这样?
  2.   

member/2的定义允许成功多次,即使找到了完全相同的解决方案。在您的列表中,所有元素都不相同。但是请考虑在末尾再出现两次的情况:

?- member(1,[1,2,3,1,1]).
true ;
true ;
true.
  
      
  1. 将立即返回的member / 2的代码是什么?
  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可能是相同的。

通过确保EX的规则不同,我们得到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) 对于 SICStusSWI扩展为:

memberd(X, [E|Es]) :-
    (   X\=E
    ->  memberd(X, Es)
    ;   X==E
    ->  true
    ;   X=E,
        true
    ;   dif(X, E),
        memberd(X, Es)
    ).

答案 1 :(得分:1)

这是第二个问题的替代答案。

  
      
  1. 将立即返回的member / 2的代码是什么?
  2.   

如果您只想检查X是否是Es的成员并且两者都为基础,则可以使用

memberchk(X, Es).

memberchk/2built-in in SWI-Prolog,但如果不是,则可以将其定义为

memberchk(X, Es) :-
    member(X, Es),
    !.

!调用后的剪切(member/2)告诉Prolog忘记了该调用可能仍然需要尝试的其他选择。

您的示例确定性(立即)成功:

?- memberchk(1, [1, 2, 3]).
true.

但是请注意,memberchk/2member/2完全不同。例如:

?- findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].

?- findall(X, memberchk(X, [1, 2, 3]), Xs).
Xs = [1].