如何表达传递关系

时间:2015-02-19 19:12:57

标签: prolog transitive-closure

我想表达一种传递关系。如果A引用B和B引用C,则A引用C.我有:

proj(A).
proj(B).
proj(C).
ref(A,B).
ref(B,C).

当我使用proj(A)查询时,我获得了:

  

[46]?-proj(A)。
  A = _639

" _639"意思?我期待是或否,并得到那种陌生感。我需要添加一条规则来说:

ref(A,C).并获得YES。理想情况下,如果可能的话,我想表明这种关系是如何产生的:(A => B => C)。

1 个答案:

答案 0 :(得分:5)

_639是一个未经实例化的匿名变量。你的“事实”有变量而不是原子。例如:

proj(A).   % This has a variable A and means "any A is a project"

所以,如果我查询:

:- proj(X).
X = _blah    % anonymous variable: anything is a project!

你需要原子:

proj(a).
proj(b).

这导致查询:

:- proj(X).
X = a ;
X = b 

如果你有:

ref(a,b).
ref(b,c).

然后,Prolog中表达传递属性的最简单方法是在Prolog中声明规则(或者所谓的谓词):

ref(A,C) :- ref(A,B), ref(B,C).

这表示,如果ref(A,C)为真,则 ref(A,B)为真,并且ref(B,C)为真。。运行查询:

:- ref(a,c).
true ;
Out of stack

或者:

:- ref(a,X).
X = b ;
X = c ;
Out of stack

所以这听起来合乎逻辑但有一个问题:由于自我引用,你可以进入一个循环。一个简单的方法是使规则名称与事实不同:

refx(A, B) :- ref(A, B).
refx(A, C) :- ref(A, B), refx(B, C).

现在,如果我查询:

:- refx(a, b).
true ;
no

:- refx(a, c).
yes

:- refx(a, X).
X = b ;
X = c
yes

但是,如果事实包含反身或交换关系,仍然存在可能存在终止问题的情况。例如:

ref(a,b).
ref(b,a).
ref(b,c).

在这种情况下,对refx(a, b)的查询会产生:

| ?- refx(a, b).
true ? ;
true ? ;
true ? ;
...

正如@ lambda.xy.x指出的那样,这可以通过跟踪我们去过的地方并避免重复“访问”来解决:

refx(X, Y) :-
    refx(X, Y, []).

refx(X, Y, A) :-
    ref(X, Y),
    \+ memberchk((X, Y), A).   % Make sure we haven't visited this case
refx(X, Y, A) :-
    ref(X, Z),
    \+ memberchk((X, Z), A),   % Make sure we haven't visited this case
    refx(Z, Y, [(X,Z)|A]).

现在我们以refx(a,b)结束并成功一次:

| ?- refx(a,b).
true ? ;
no
| ?-

并且refx(X, Y)产生了所有解决方案(尽管由于成功不止一次而重复一些):

| ?- refx(X, Y).

X = a
Y = b ? ;

X = b
Y = a ? ;

X = b
Y = c ? ;

X = a
Y = a ? ;

X = a
Y = c ? ;

X = b
Y = b ? ;

X = b
Y = c ? ;

(2 ms) no