Prolog 序言:从类图问题中删除圆

Prolog 序言:从类图问题中删除圆,prolog,Prolog,我有下面的prolog程序,我想从位置a转到位置d。我们可以通过以下路径来实现:a->b->c->d。另一个路径是:a->b->c->b->c->d等。我们如何删除“循环”路径?我试图通过使用'not(member(from_to(X,Z))'来删除它,但它似乎不起作用 from_to(a, b). from_to(b, c). from_to(c, d). from_to(d, c). from_to(c, b). move(X,Y,Z) :- from_to(X,Y), X \= Y,

我有下面的prolog程序,我想从位置a转到位置d。我们可以通过以下路径来实现:a->b->c->d。另一个路径是:a->b->c->b->c->d等。我们如何删除“循环”路径?我试图通过使用'not(member(from_to(X,Z))'来删除它,但它似乎不起作用

from_to(a, b).
from_to(b, c).
from_to(c, d).
from_to(d, c).
from_to(c, b).

move(X,Y,Z) :- from_to(X,Y), X \= Y, 
               Z = [from_to(X,Y)].      
move(X,Y,Z) :- from_to(X,K), K \= Y, move(K,Y,Z1), 
               Z = [from_to(X,K)|Z1],   
               not(member(from_to(X,_),Z)).
(如果删除行“not(member(从_到(X,Z)),程序工作正常,但输出循环路径)

这里最好使用累加器:一个通过递归调用更新的变量,因此包含某种“内存”。这里累加器可以存储我们访问过的节点列表。要移动到新节点,该节点不应在列表中

因此,我们定义一个谓词
move/4
,而不是
move/3
,带有:

move(X,Y,Z) :-
    move(X, Y, Z, [X]).
现在我们可以使用两个规则定义谓词
move(S,D,Path,visted)

  • 如果
    S
    D
    相同,我们就完成了,不管
    访问的
    是什么,我们都将
    路径
    [D]
    统一起来;及
  • 否则,我们通过
    from_to/2
    谓词“走”到另一个节点
    N
    ,确保它不是
    访问的
    的成员,然后我们进行递归调用,将
    S
    前置到
    N
    访问的节点。我们在递归
    Z
    的结果前面加上
    X
  • 例如:

    move(S, S, [S], _).
    move(S, D, [S|Z], Visited) :-
        from_to(S, N),
        \+ member(N, Visited),
        move(N, D, Z, [N|Visited]).
    
    对于示例图形:

    然后生成:

    ?- move(a, d, Z).
    Z = [a, b, c, d] ;
    false.
    
    ?- move(a, D, Z).
    D = a,
    Z = [a] ;
    D = b,
    Z = [a, b] ;
    D = c,
    Z = [a, b, c] ;
    D = d,
    Z = [a, b, c, d] ;
    false.
    
    ?- move(A, d, Z).
    A = d,
    Z = [d] ;
    A = a,
    Z = [a, b, c, d] ;
    A = b,
    Z = [b, c, d] ;
    A = c,
    Z = [c, d] ;
    false.
    
    ?- move(A, D, Z).
    A = D,
    Z = [D] ;
    A = a,
    D = b,
    Z = [a, b] ;
    A = a,
    D = c,
    Z = [a, b, c] ;
    A = a,
    D = d,
    Z = [a, b, c, d] ;
    A = b,
    D = c,
    Z = [b, c] ;
    A = b,
    D = d,
    Z = [b, c, d] ;
    A = c,
    D = d,
    Z = [c, d] ;
    A = d,
    D = c,
    Z = [d, c] ;
    A = d,
    D = b,
    Z = [d, c, b] ;
    A = c,
    D = b,
    Z = [c, b] ;
    false.
    
    如果节点未“连接到自身”,例如,我们没有从
    a
    a
    的路径,我们可以实现
    move
    ,如下所示:

    move(S, D, [S|Z], V) :-
        from_to(S, N),
        \+ member(N, V),
        move2(N, D, Z, [N|V]).
    
    move2(S, S, [S], _).
    move2(N, D, [S|Z], V) :-
        move(N, D, Z, V).
    

    您需要保留一个包含已访问节点的列表(最初为空)。另外,使用标准的
    \+/1
    否定运算符/谓词,而不是不推荐的
    非/1
    谓词。
    路径(从u到,路径,X,Z)
    使用。请注意,已访问节点的列表更加紧凑,包含的信息与从\u到/2的
    术语列表相同。非常感谢!我明天会发布解决方案(如果没有其他人发布)。