簡體   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