路径/步道/步行的定义

时间:2015-05-19 14:24:09

标签: graph prolog transitive-closure meta-predicate

许多谓词定义了某种非循环路径,这种路径是通过二元关系定义的边缘构建的,与defining transitive closure非常相似。因此需要通用定义。

请注意,图论中定义的概念不容易与通常预期的匹配。最值得注意的是,我们对边缘的名称不感兴趣。

更糟糕的是,图论也发生了一些变化,引入了walk的概念,注意到

  

传统上,路径指的是现在通常称为开放式步行的路径。如今,当没有任何限定地陈述时,路径通常被理解为简单的,意味着没有重复顶点(因此没有边缘)。 (术语链也用于指代所有顶点和边缘都不同的步行。)

所以我的问题是:如何命名和定义此功能?

到目前为止我所做的是定义:

path(Rel_2, Path, X0,X)

第一个论点必须是关系的延续。然后是Path或顶点对。

使用示例

n(a, b).
n(b, c).
n(b, a).

?- path(n,Xs, a,X).
Xs = [a], X = a ;
Xs = [a, b], X = b ;
Xs = [a, b, c], X = c ;
false.

实施

:- meta_predicate path(2,?,?,?).

:- meta_predicate path(2,?,?,?,+).

path(R_2, [X0|Ys], X0,X) :-
   path(R_2, Ys, X0,X, [X0]).

path(_R_2, [], X,X, _).
path(R_2, [X1|Ys], X0,X, Xs) :-
   call(R_2, X0,X1),
   non_member(X1, Xs),
   path(R_2, Ys, X1,X, [X1|Xs]).

non_member(_E, []).
non_member(E, [X|Xs]) :-
   dif(E,X),
   non_member(E, Xs).

3 个答案:

答案 0 :(得分:11)

我想专注于命名谓词。

  • maplist/2不同, 这里的论证顺序并不重要。

  • 谓词名称应使各个参数的含义清晰。

到目前为止,我最喜欢path_from_to_edges,但它也有其优点和缺点。

path_from_to_edges(Path,From,To,Edges_2) :-
    path(Edges_2,Path,From,To).

让我们分开吧:

  • 亲:path是一个名词,它不能被误读为动词。对我来说,暗示了一个顶点列表。

  • 亲:from代表顶点,to代表。

  • con:edges 有些模糊,但在这里使用lambdas是最通用的选择。

  • con:根据Wikipedia,路径是一条路径,其中所有顶点(除了第一个和最后一个之外)都是不同的。因此,需要在说明中予以澄清。

将lambdas用于邻居顶点列表Ess

?- Ess  = [a-[b],b-[c,a]], 
   From = a,
   path_from_to_edges(Path,From,To,\X^Y^(member(X-X_neibs,Ess),member(Y,X_neibs))).
Ess = [a-[b],b-[c,a]], From = a, To = a, Path = [a]     ;
Ess = [a-[b],b-[c,a]], From = a, To = b, Path = [a,b]   ;
Ess = [a-[b],b-[c,a]], From = a, To = c, Path = [a,b,c] ;
false.

编辑2015-06-02

更好的命名的另一个镜头!这更倾向于maplist/2 ......

graph_path_from_to(P_2,Path,From,To) :-
   path(P_2,Path,From,To).

在这里,graph当然是名词而不是动词。

关于" path"的含义:路径肯定应该允许From=To并且默认情况下不排除(使用成对术语不等式)。通过额外的dif(From,To)目标很容易将其排除在外,但不是相反。

答案 1 :(得分:10)

如此定义path/4怎么样?

path(R_2, Xs, A,Z) :-                   % A path `Xs` from `A` to `Z` is ...
   walk(R_2, Xs, A,Z),                  % ... a walk `Xs` from `A` to `Z` ...
   all_dif(Xs).                         % ... with no duplicates in `Xs`.

为了帮助普遍终止,我们在上面交换了两个目标......

path(R_2, Xs, A,Z) :-
   all_dif(Xs),                         % enforce disequality ASAP
   walk(R_2, Xs, A,Z).

...并使用以下all_dif/1的惰性实现:

all_dif(Xs) :-                          % enforce pairwise term inequality
   freeze(Xs, all_dif_aux(Xs,[])).      % (may be delayed)

all_dif_aux([], _).
all_dif_aux([E|Es], Vs) :-               
   maplist(dif(E), Vs),                 % is never delayed
   freeze(Es, all_dif_aux(Es,[E|Vs])).  % (may be delayed)

walk/4的定义类似于OP提供的path/4path/5

:- meta_predicate walk(2, ?, ?, ?).
walk(R_2, [X0|Xs], X0,X) :-
   walk_from_to_step(Xs, X0,X, R_2).

:- meta_predicate walk_from_to_step(?, ?, ?, 2).
walk_from_to_step([], X,X, _).
walk_from_to_step([X1|Xs], X0,X, R_2) :-
   call(R_2, X0,X1),
   walk_from_to_step(Xs, X1,X, R_2).

path/4以上的IMO更简单,更平易近人,特别是对于初学者。你会同意吗?

答案 2 :(得分:4)

我没有看到在path / 4中定义参数的原因" start node"和"结束节点"。似乎带有规则和节点列表的简单路径/ 2必须足够。

如果用户想要一个以某个节点开头的列表(例如' a'),他可以将该语句查询为:path(some_rule,[' a' | Q] )。

例如,用户可以请求路径长度为10的路径:长度(P,10),路径(some_rule,P)。

*附录1 *

可以轻松添加一些实用目标,但它们不是主要主题。例如,带有起始节点的路径/ 3是:

path( some_rule, [start|Q], start ) :- 
  path ( some_rule, [start|Q ] ).   

*附录2 *

添加最后一个节点作为参数可能会错误地认为这个参数驱动了算法,但它并没有。假设通过示例:

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

并跟踪查询的算法执行:

[trace]  ?- path( n, P, X, d ).
   Call: (6) path(n, _G1025, _G1026, d) ? creep
   Call: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Exit: (7) path(n, [], d, d, [d]) ? creep
   Exit: (6) path(n, [d], d, d) ? creep
P = [d],
X = d ;
   Redo: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Call: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, b) ? creep

   Call: (8) non_member(b, [a]) ? creep
   Call: (9) dif:dif(b, a) ? creep
   Exit: (9) dif:dif(b, a) ? creep
   Call: (9) non_member(b, []) ? creep
   Exit: (9) non_member(b, []) ? creep
   Exit: (8) non_member(b, [a]) ? creep
   Call: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Call: (9) n(b, _G1118) ? creep
   Fail: (9) n(b, _G1118) ? creep
   Fail: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Redo: (9) non_member(b, []) ? creep
   Fail: (9) non_member(b, []) ? creep
   Fail: (8) non_member(b, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, c) ? creep

   Call: (8) non_member(c, [a]) ? creep
   Call: (9) dif:dif(c, a) ? creep
   Exit: (9) dif:dif(c, a) ? creep
   Call: (9) non_member(c, []) ? creep
   Exit: (9) non_member(c, []) ? creep
   Exit: (8) non_member(c, [a]) ? creep
   Call: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Call: (9) n(c, _G1118) ? creep
   Fail: (9) n(c, _G1118) ? creep
   Fail: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Redo: (9) non_member(c, []) ? creep
   Fail: (9) non_member(c, []) ? creep
   Fail: (8) non_member(c, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, d) ? creep

   Call: (8) non_member(d, [a]) ? creep
   Call: (9) dif:dif(d, a) ? creep
   Exit: (9) dif:dif(d, a) ? creep
   Call: (9) non_member(d, []) ? creep
   Exit: (9) non_member(d, []) ? creep
   Exit: (8) non_member(d, [a]) ? creep
   Call: (8) path(n, _G1113, d, d, [d, a]) ? creep
   Exit: (8) path(n, [], d, d, [d, a]) ? creep
   Exit: (7) path(n, [d], a, d, [a]) ? creep
   Exit: (6) path(n, [a, d], a, d) ? creep
P = [a, d],
X = a .

正如您所看到的,在这种情况下,算法无法蛮力。 因此,如果算法没有改进,我建议不要添加" end node"作为"路径"参数。