繁体   English   中英

Prolog比较变量的方法

[英]Prolog ways to compare variables

我试图在Prolog中实现一些图算法。 我提出了一个使用统一来构建图结构树的想法:

该图将定义如下:

  • Vertex-Variable对列表,其中“ Vertex是表示顶点的常量,“ Variable是相应的变量,可用作顶点的“参考”。 例如:

    [aA, bB, cC, dD]

  • VertexVar-NeighboursList对的列表,其中VertexVarNeighboursList中的各个邻居是“参考变量”。 例如:

    [A-[B, C, D], B-[A, C], C-[A, B], D-[A]]意思bcd是邻居a

然后在一些图形算法(如搜索组件,或简单的DFS / BFS等)之前可以使用从原始图形构建的某种树,可以使用一些谓词,如unify_neighbours ,将VertexVar-NeighbourList对统一为VertexVar = NeighboursList 之后,顶点变量可以被解释为其邻居的列表,其中每个邻居再次是其邻居的列表。

因此,这将在遍历图形时产生良好的性能,因为不需要对图形中的每个顶点的某些顶点及其邻域进行线性搜索。

但我的问题是:如何比较这些顶点变量? (检查它们是否相同。)我尝试使用A == B ,但存在一些冲突。 对于上面的示例,(使用unify_neighbours谓词)Prolog在内部将图解释为:

[a-[S_1, S_2, S_3], b-S_1, c-S_2, d-S_3]

哪里:

S_1 = [[S_1, S_2, S_3], S_2]
S_2 = [[S_1, S_2, S_3], S_1]
S_3 = [[S_1, S_2, S_3]]

问题在于S_1S_2 (又名bc ),因为X = [something, Y], Y = [something, X], X == Ytrue 同样的问题是顶点,它们共享相同的邻居。 例如U-[A, B]V-[A, B]

所以我的问题是:有没有其他方法来比较变量,这可以帮助我吗? 比较“变量本身”而不是内容的东西,比如在程序编程语言中比较地址? 或者这是否过于程序化并打破Prolog的陈述性思想?

graph_component(Vertices, Neighbours, C) :-
    % Vertices and Neighbours as explained above.
    % C is some component found in the graph.
    vertices_refs(Vertices, Refs),
    % Refs are only the variables from the pairs.
    unify_neighbours(Neighbours), % As explained above.
    rec_(Vertices, Refs, [], C).

rec_(Vertices, Refs, Found, RFound) :-
    % Vertices as before.
    % Refs is a stack of the vertex variables to search.
    % Found are the vertices found so far.
    % RFound is the resulting component found.
    [Ref|RRest] = Refs,
    vertices_pair(Vertices, Vertex-Ref),
    % Vertex is the corresponding Vertex for the Ref variable
    not(member(Vertex, Found)),
    % Go deep:
    rec_(Vertices, Ref, [Vertex|Found], DFound),
    list_revpush_result([Vertex|Found], DFound, Found1),
    % Go wide:
    rec_(Vertices, RRest, Found1, RFound).

rec_(Vertices, Refs, Found, []) :-
    % End of reccursion.
    [Ref|_] = Refs,
    vertices_pair(Vertices, Vertex-Ref),
    member(Vertex, Found).

这个例子并没有真正起作用,但这是个主意。 (另外,检查顶点是否被找到是线性完成的,所以性能仍然不好,但它仅用于演示。)现在,查找变量的相应顶点的谓词实现为:

vertices_pair([Vertex-Ref|_], Vertex-Ref).
vertices_pair([_-OtherRef|Rest], Vertex-Ref) :-
    Ref \== OtherRef,
    vertices_pair(Rest, Vertex-Ref).

\\==运算符不是我想要的,它会产生这些冲突。

Prolog的一个固有特征是, 一旦你将变量绑定到一个术语,它就会与术语本身无法区分 换句话说,如果将两个变量绑定到同一个术语,则有两个相同的东西,并且没有办法区分它们。

应用于您的示例:一旦您将每个顶点变量与相应的邻居列表统一起来,所有变量都将消失:您只需使用嵌套(并且很可能是循环)数据结构,该列表包含列表列表...

但正如您所建议的那样,嵌套结构是一个很有吸引力的想法,因为它可以让您直接访问相邻节点。 虽然Prolog系统在支持循环数据结构方面有所不同,但这并不能阻止你利用这个想法。

设计的唯一问题是节点完全由(可能深度嵌套和循环)数据结构识别,该数据结构描述了可从其访问的子图。 这导致了这样的结果

  • 具有相同后代的两个节点是无法区分的
  • 检查两个“相似的”子图是否相同可能是非常昂贵的

一种简单的方法是在数据结构中包含唯一的节点标识符 (例如名称或编号)。 要使用您的示例(稍加修改以使其更有趣):

make_graph(Graph) :-
    Graph = [A,B,C,D],
    A = node(a, [C,D]),
    B = node(b, [A,C]),
    C = node(c, [A,B]),
    D = node(d, [A]).

然后,您可以使用该标识符来检查匹配的节点,例如在深度优先遍历中:

dfs_visit_nodes([], Seen, Seen).
dfs_visit_nodes([node(Id,Children)|Nodes], Seen1, Seen) :-
    ( member(Id, Seen1) ->
        Seen2 = Seen1
    ;
        writeln(visiting(Id)),
        dfs_visit_nodes(Children, [Id|Seen1], Seen2)
    ),
    dfs_visit_nodes(Nodes, Seen2, Seen).

样品运行:

?- make_graph(G), dfs_visit_nodes(G, [], Seen).
visiting(a)
visiting(c)
visiting(b)
visiting(d)

G = [...]
Seen = [d, b, c, a]
Yes (0.00s cpu)

谢谢,@ jschimpf,答案。 它为我澄清了很多东西。 我刚刚回到Prolog的一些图形问题,并认为我会给这个递归数据结构另一次尝试,并提出以下谓词从边列表构造这个数据结构:

由@jschimpf提出的“手动”创建数据结构:

my_graph(Nodes) :-
    Vars  = [A, B, C, D, E],
    Nodes = [
        node(a, [edgeTo(1, B), edgeTo(5, D)]),
        node(b, [edgeTo(1, A), edgeTo(4, E), edgeTo(2, C)]),
        node(c, [edgeTo(2, B), edgeTo(6, F)]),
        node(d, [edgeTo(5, A), edgeTo(3, E)]),
        node(e, [edgeTo(3, D), edgeTo(4, B), edgeTo(1, F)]),
        node(e, [edgeTo(1, E), edgeTo(6, C)])
    ],
    Vars = Nodes.

其中edgeTo(Weight, VertexVar)表示某个顶点的边,其重量与其相关。 重量只是为了表明这可以为任何其他信息定制。 node(Vertex, [edgeTo(Weight, VertexVar), ...])表示一个带有邻居的顶点。

更“用户友好”的输入格式:

[edge(Weight, FromVertex, ToVertex), ...]

使用可选的顶点列表:

[Vertex, ...]

对于上面的例子:

[edge(1, a, b), edge(5, a, d), edge(2, b, c), edge(4, b, e), edge(6, c, f), edge(3, d, e), edge(1, e, f)]

可以使用以下谓词将此列表转换为递归数据结构:

% make_directed_graph(+Edges, -Nodes)
make_directed_graph(Edges, Nodes) :-
    vertices(Edges, Vertices),
    vars(Vertices, Vars),
    pairs(Vertices, Vars, Pairs),
    nodes(Pairs, Edges, Nodes),
    Vars = Nodes.

% make_graph(+Edges, -Nodes)
make_graph(Edges, Nodes) :-
    vertices(Edges, Vertices),
    vars(Vertices, Vars),
    pairs(Vertices, Vars, Pairs),
    directed(Edges, DiretedEdges),
    nodes(Pairs, DiretedEdges, Nodes),
    Vars = Nodes.

% make_graph(+Edges, -Nodes)
make_graph(Edges, Nodes) :-
    vertices(Edges, Vertices),
    vars(Vertices, Vars),
    pairs(Vertices, Vars, Pairs),
    directed(Edges, DiretedEdges),
    nodes(Pairs, DiretedEdges, Nodes),
    Vars = Nodes.

% make_directed_graph(+Vertices, +Edges, -Nodes)
make_directed_graph(Vertices, Edges, Nodes) :-
    vars(Vertices, Vars),
    pairs(Vertices, Vars, Pairs),
    nodes(Pairs, Edges, Nodes),
    Vars = Nodes.

这些谓词的二进制版本假设每个顶点只能从边列表中获得 - 图中没有“无边缘”顶点。 对于这些情况,三元版本采用额外的顶点列表。

make_directed_graph假定要定向的输入边, make_graph假定它们是无向的,因此它会在相反的方向上创建额外的有向边:

% directed(+UndirectedEdges, -DiretedEdges)
directed([], []).
directed([edge(W, A, B)|UndirectedRest], [edge(W, A, B), edge(W, B, A)|DirectedRest]) :-
    directed(UndirectedRest, DirectedRest).

要从边列表中获取所有顶点:

% vertices(+Edges, -Vertices)
vertices([], []).
vertices([edge(_, A, B)|EdgesRest], [A, B|VerticesRest]) :-
    vertices(EdgesRest, VerticesRest),
    \+ member(A, VerticesRest),
    \+ member(B, VerticesRest).
vertices([edge(_, A, B)|EdgesRest], [A|VerticesRest]) :-
    vertices(EdgesRest, VerticesRest),
    \+ member(A, VerticesRest),
    member(B, VerticesRest).
vertices([edge(_, A, B)|EdgesRest], [B|VerticesRest]) :-
    vertices(EdgesRest, VerticesRest),
    member(A, VerticesRest),
    \+ member(B, VerticesRest).
vertices([edge(_, A, B)|EdgesRest], VerticesRest) :-
    vertices(EdgesRest, VerticesRest),
    member(A, VerticesRest),
    member(B, VerticesRest).

为每个顶点构造未初始化的变量:

% vars(+List, -Vars)
vars([], []).
vars([_|ListRest], [_|VarsRest]) :-
    vars(ListRest, VarsRest).

要配对verticies和顶点变量:

% pairs(+ListA, +ListB, -Pairs)
pairs([], [], []).
pairs([AFirst|ARest], [BFirst|BRest], [AFirst-BFirst|PairsRest]) :-
    pairs(ARest, BRest, PairsRest).

构造递归节点:

% nodes(+Pairs, +Edges, -Nodes)
nodes(Pairs, [], Nodes) :-
    init_nodes(Pairs, Nodes).
nodes(Pairs, [EdgesFirst|EdgesRest], Nodes) :-
    nodes(Pairs, EdgesRest, Nodes0),
    insert_edge(Pairs, EdgesFirst, Nodes0, Nodes).

首先,初始化每个顶点的空节点列表:

% init_nodes(+Pairs, -EmptyNodes)
init_nodes([], []).
init_nodes([Vertex-_|PairsRest], [node(Vertex, [])|NodesRest]) :-
    init_nodes(PairsRest, NodesRest).

然后逐个插入边缘:

% insert_edge(+Pairs, +Edge, +Nodes, -ResultingNodes)
insert_edge(Pairs, edge(W, A, B), [], [node(A, [edgeTo(W, BVar)])]) :-
    vertex_var(Pairs, B, BVar).
insert_edge(Pairs, edge(W, A, B), [node(A, EdgesTo)|NodesRest], [node(A, [edgeTo(W, BVar)|EdgesTo])|NodesRest]) :-
    vertex_var(Pairs, B, BVar).
insert_edge(Pairs, edge(W, A, B), [node(X, EdgesTo)|NodesRest], [node(X, EdgesTo)|ResultingNodes]) :-
    A \= X,
    insert_edge(Pairs, edge(W, A, B), NodesRest, ResultingNodes).

获取给定顶点的顶点变量:(这实际上适用于两个方向。)

% vertex_var(+Pairs, +Vertex, -Var)
vertex_var(Pairs, Vertex, Var) :-
    member(Vertex-Var, Pairs).
```Prolog

This, of course, brings additional time overhead, but you can do this once and then just copy this data structure every time you need to perform some graph algorithm on it and access neighbours in constant time.

You can also add additional information to the `node` predicate. For example:

```Prolog
node(Vertex, Neighbours, OrderingVar)

例如,未初始化变量OrderingVar可以在恒定时间内“分配”(初始化),并且具有关于图形的部分排序中的顶点位置的信息。 所以这可以用作输出。 (在Prolog注释中有时用+-表示+-作为输入项的一部分的未初始化变量,尚未由使用的谓词初始化并提供输出。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM