Reference Prolog中比较变量的方法

Reference Prolog中比较变量的方法,reference,prolog,logic-programming,declarative-programming,Reference,Prolog,Logic Programming,Declarative Programming,我试图在Prolog中实现一些图形算法。我想到了一个使用统一从图形结构构建树的想法: 该图的定义如下: 顶点变量对的列表,其中顶点是表示顶点的常数,变量是对应的变量,将用作顶点的“参考”。e、 g: [a-a,b-b,c-c,d-d] VertexVar-neighturslist对的列表,其中VertexVar和neighturslist中的各个邻居是“参考变量”。e、 g: [A-[B,C,D],B-[A,C],C-[A,B],D-[A]意思是B,C,D是A的邻居 然后,在一些可以使用从

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

该图的定义如下:

  • 顶点变量
    对的列表,其中
    顶点
    是表示顶点的常数,
    变量
    是对应的变量,将用作顶点的“参考”。e、 g:

    [a-a,b-b,c-c,d-d]

  • VertexVar-neighturslist
    对的列表,其中
    VertexVar
    neighturslist
    中的各个邻居是“参考变量”。e、 g:

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

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

因此,这将在遍历图时产生良好的性能,因为不需要对图中的每个顶点进行线性搜索,也不需要对每个顶点进行线性搜索

但我的问题是:如何比较这些顶点变量?(检查它们是否相同。)我尝试使用
A==B
,但存在一些冲突。对于上面的示例,(使用
unify_
谓词)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_1
S_2
(又称
b
c
),因为
X=[something,Y],Y=[something,X],X==Y
真的。同样的问题也会出现在共享相同邻居的顶点上。e、 g.
U-[A,B]
V-[A,B]

所以我的问题是:有没有其他方法来比较变量,可以帮助我解决这个问题?比较“变量本身”,而不是内容的东西,比如比较过程编程语言中的地址?或者这会不会太程序化,打破了Prolog的声明性思想

例子 这个例子并没有真正起作用,但它的想法。(另外,检查是否找到顶点是线性的,因此性能仍然不好,但只是为了演示。)现在,为变量找到对应顶点的谓词实现为:

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

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

Prolog的一个固有特性是,一旦将变量绑定到一个术语,它就无法与术语本身区分开来。换句话说,如果你把两个变量绑定到同一个项上,你就有了两个完全相同的东西,并且没有办法区分它们

应用于您的示例:一旦将每个顶点变量与相应的邻域列表统一,所有变量都将消失:只剩下一个嵌套(最可能是循环)数据结构,由列表列表组成

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

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

  • 具有相同子体的两个节点无法区分
  • 检查两个“外观相似”的子图是否相同可能非常昂贵
一种简单的解决方法是在数据结构中包含一个唯一节点标识符(例如名称或编号)。要使用您的示例(稍作修改以使其更有趣),请执行以下操作:

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

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的一些图形问题上,我想我应该再尝试一下这个递归数据结构,并提出了以下谓词来从边列表构造这个数据结构:

% 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).
@jsf建议的数据结构的“手动”创建:

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)
表示某个顶点的边,其权重与其关联。重量只是为了表明这可以为任何附加信息定制<代码>节点(顶点,[edgeTo(权重,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).
要将垂直和顶点变量配对,请执行以下操作:

% 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).
首先,创建每个顶点的空节点列表
% 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)