Prolog 路径/小径/步行的定义
许多谓词定义某种非循环路径,该路径是由通过二进制关系定义的边构建的,非常类似于。因此需要一个通用的定义 请注意,图论中定义的概念并不容易与通常的预期相匹配。最值得注意的是,我们对边的名称不感兴趣 更糟糕的是,图论也发生了一些变化,引入了,注意的概念 传统上,小径指的是现在通常所说的开放式步行道。现在,如果在没有任何限定条件的情况下陈述,路径通常被理解为简单的,这意味着没有顶点(因此也没有边)重复。(术语链也用于指所有顶点和边都不同的行走。) 所以我的问题是:如何命名和定义这个功能 到目前为止,我所做的是定义:Prolog 路径/小径/步行的定义,prolog,graph-theory,transitive-closure,meta-predicate,Prolog,Graph Theory,Transitive Closure,Meta Predicate,许多谓词定义某种非循环路径,该路径是由通过二进制关系定义的边构建的,非常类似于。因此需要一个通用的定义 请注意,图论中定义的概念并不容易与通常的预期相匹配。最值得注意的是,我们对边的名称不感兴趣 更糟糕的是,图论也发生了一些变化,引入了,注意的概念 传统上,小径指的是现在通常所说的开放式步行道。现在,如果在没有任何限定条件的情况下陈述,路径通常被理解为简单的,这意味着没有顶点(因此也没有边)重复。(术语链也用于指所有顶点和边都不同的行走。) 所以我的问题是:如何命名和定义这个功能 到目前为止,我
path(Rel_2, Path, X0,X)
第一个参数必须是关系的延续。然后是路径
或一对顶点
示例用法
实施
我看不出在path/4中定义参数“start node”和“end node”的原因。似乎一个简单的路径/2和规则以及节点列表就足够了 如果用户想要一个以某个节点(例如“a”)开头的列表,他可以查询如下语句:path(some_规则,['a'| Q]) 例如,用户可以请求长度为10的路径:length(p,10),path(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”添加为“path”参数。我想重点关注谓词的命名
- 不像, 参数顺序在这里不是最重要的
- 谓词名称应该使各个参数的含义清晰
path_from_to_edges(Path,From,To,Edges_2) :-
path(Edges_2,Path,From,To).
让我们把它分开:
- pro:
是一个名词,不能误读为动词。对我来说,暗示了顶点列表path
- pro:
代表顶点,from
也代表顶点to
- 缺点:
有些模糊,但在这里使用是最通用的选择edges
- 缺点:根据,路径是所有顶点(第一个和最后一个可能除外)都不同的轨迹。因此,这需要在描述中加以澄清
将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
当然是一个名词,而不是动词
关于“路径”的含义:路径绝对应该允许
From=To
,默认情况下不排除这一点(使用成对的术语不等式)。用一个额外的dif(From,to)
目标很容易排除这一点,但反过来就不行了。这样定义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)
上面的IMO
path/4
更简单、更容易接近,特别是对于新手。你会同意吗?是路径/5尾递归吗?@pasa:只有在目标调用(R_2,X0,X1)
是确定的情况下。你应该找到一种方法来接受这个问题的答案,以增加其在搜索结果中的可见性。我知道它在那里,所以我通常会搜索“[prolog]路径跟踪”,但在不太具体的搜索中,它显示得并没有它应该显示的那么高,特别是考虑到问题本身有多少选票。@Boris:到目前为止,已经花了很多时间来提高可见性。我只把我的观点放在上面10k@XXX:无论如何,这是一个遗憾。不提供额外的两个参数会使许多查询变得麻烦。例如:从a
到b
存在哪些路径?这将读作:Path=[a | |,Path(connex,Path),last(Path,b)
将其与Path(connex,Path,a,b)
对比您提供的示例显示:a)启动节点不是必需的;只有在使用更有效的算法之前,结束节点才是必需的。结束情况,以及通常的任何中间或最终情况,都需要比原始消息中提供的算法更好的算法。继续,很好!那么请提供一些!广告附录2:这是一个准确的观察结果。事实上,结束节点是一个稳定的参数。然而,对于一个完整的搜索,没有什么是可以期待的<代码>边建议一个边列表。缺点:与Richard O'Keefe的论点排序相反,后者将元论点放在很早的时候。此外,元参数可能没有变化,因此人们永远不会执行调用(path_from_to_edges(path,from,to),P_2)
类似于映射列表(path_from_to_edges(path,from,to),p2s)
和边
表示边的列表
——虽然我们没有一些DIF
通常称为所有不同的
或所有不同的
@false。两全其美!谢谢!
path(R_2, Xs, A,Z) :-
all_dif(Xs), % enforce disequality ASAP
walk(R_2, Xs, A,Z).
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)
:- 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).