如何在不回溯所有不同路径的情况下检查 Prolog 中是否存在任何统计子句?

SQB*_*SQB 5 prolog prolog-cut prolog-setof

假设我有以下内容:

parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).    

% people are siblings of each other if they share a parent 
% and aren't the same person.
sibling(A, B) :-
  parent(X, A),
  parent(X, B),
  B \= A.
Run Code Online (Sandbox Code Playgroud)

现在如果我问黛安的兄弟姐妹,我会得到查理和夏娃——两次,一次通过鲍勃,一次通过爱丽丝。我只想要一次。
我不认为我可以在这里使用削减,因为这会完全防止回溯。我想,是检查的方式任何存在。

释义

sibling(A, B) :-
  ?(parent(X, A), parent(X, B)),
  B \= A.
Run Code Online (Sandbox Code Playgroud)

我尝试了几次切割,但都没有奏效。
我试图findall/3(parent(X, A), parent(X, B))和检查,如果结果列表是非空的,但这并不统一A或B.


setof/3按照下面的建议使用是有效的,但我真的想找到一种方法将它合并到 的定义中sibling/2,而不必在问题中使用它。我真的很想能够做到以下几点:

?- sibling(diane, X).
X = charlie ;
X = eve ;
false.
Run Code Online (Sandbox Code Playgroud)

或这个

?sibling(X, Y).
X = charlie,
Y = diane ;
X = charlie,
Y = eve ;
X = diane,
Y = charlie ;
X = diane,
Y = eve ;
X = eve,
Y = charlie ;
X = eve,
Y = diane ;
false.
Run Code Online (Sandbox Code Playgroud)

就像我在下面说的,我有针对这种特定情况的解决方案。我想要的是一个通用的解决方案,我正在为此设置赏金。

代替

sibling(A, B) :-
  setof(D, X^(parent(X, A), parent(X, D)), Ds),
  member(B, Ds),
  B \= A.
Run Code Online (Sandbox Code Playgroud)

我想做

sibling(A, B) :-
  exists(X^(parent(X, A), parent(X, B))),
  B \= A.
Run Code Online (Sandbox Code Playgroud)

它统一了AB

我如何定义exists/1

fal*_*lse 5

在 Prolog 中使用 cut 是非常微妙的。大多数削减基本上是不正确的,但在某些情况下仍然有效。您可以在此处使用剪切,前提是您只需要一个答案。但是由于您想要整个集合,所以您不走运:您需要探索所有答案以确定该集合。

幸运的是,有一个优雅的快捷方式(双关语):setof/3. 所以问

?- setof(t, sibling(diane, S), _).
Run Code Online (Sandbox Code Playgroud)

对于 的这种用法setof/3,最后一个参数没有意义。它实际上是[t]

对于一般目的exists/1,定义

exists(XGoal) :- setof(t, XGoal, _).
Run Code Online (Sandbox Code Playgroud)

这允许使用存在量词。

  • @SQB:setof 中的 `_` 通常是一个错误(在第二个参数中)。是的,它也适用于第一个参数 `_`,但在这里你正在探索 `setof/3` 的一些非常可怕的限制。最好保留一些原子。`t` 非常地道。 (4认同)
  • 或者: `exists(XGoal) :- setof(t,XGoal,_)` 现在你甚至可以使用量词。 (2认同)
  • 是的,这有效!我可以发誓这不会统一,但确实如此。我一定是在什么地方搞砸了。无论如何,赏金赚了:) (2认同)