erlang 如何将一个有向图传递给不同的流程和节点?

cidc1ykv  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(139)

我在进程A上创建了一个有向图项,我想将此有向图传递给另一个节点上的进程。每当我在另一个进程上使用此有向图时,都会出现如下错误:

** {badarg,
   [{ets,insert,[598105,{"EPqzYxiM9UV0pplPTRg8vX28h",[]}],[]},
    {digraph,do_add_vertex,2,[{file,"digraph.erl"},{line,377}]},

因为有向图是基于ETS的,这看起来要复杂得多,使得有向图在它创建的过程中几乎是独立的。我发现下面的条目揭示了一个类似的问题:ETS on a different process
我知道我可以在一个服务器中创建有向图,然后通过OTP消息连接到它,但在我的体系结构中我无法做到这一点。所有节点都可以使用一种特定的方法进行通信,这种方法被设计为将状态作为术语传递。
在我看来,将有向图发送到不能直接相互通信的不同节点是不可能的。总的来说,有向图似乎不能直接序列化。我在想,我可以将有向图“展开”为顶点和边的列表,然后在其他进程上传输和重新创建它(效率不高,执行不好,或者不优雅)。有没有更好的方法来序列化它?有没有方法可以序列化ETS存储之外的有向图状态?
有什么想法吗?

bq3bfh9z

bq3bfh9z1#

您可以序列化/反序列化一个有向图,如下所示;

serialize({digraph, V, E, N, B}) ->
    {ets:tab2list(V),
     ets:tab2list(E),
     ets:tab2list(N),
     B}.

deserialize({VL, EL, NL, B}) ->       
    DG = {digraph, V, E, N, B} = case B of 
       true -> digraph:new();
       false -> digraph:new([acyclic])
    end,
    ets:delete_all_objects(V)
    ets:delete_all_objects(L)
    ets:delete_all_objects(N)
    ets:insert(V, VL)
    ets:insert(E, EL)
    ets:insert(N, NL)
    DG.

这是我用来测试的代码

passer() ->
    G = digraph:new(),
    V1 = digraph:add_vertex(G),
    V2 = digraph:add_vertex(G),
    V3 = digraph:add_vertex(G),
    digraph:add_edge(G, V1, V2, "edge1"),
    digraph:add_edge(G, V1, V3, "edge2"),
    Pid = spawn(fun receiver/0),
    Pid ! serialize(G).

receiver() ->
     receive
         SG = {_VL, _EL, _NL, _B} ->
            G = deserialize(SG),
            io:format("Edges: ~p~n", [digraph:edges(G)]),
            io:format("Edges: ~p~n", [digraph:vertices(G)])
     end.

很难看的解决方案,但是可以工作。我认为这是在节点之间传递有向图的唯一方法,因为ets表不能在节点之间共享。
编辑:移除不必要的循环

1yjd4xko

1yjd4xko2#

我有一个解决方案,但是它依赖于digraph:new()返回的变量的结构,所以我不确定它是否与未来的版本兼容。

D = digraph:new(),
...
%some code modifying D
...
{digraph,Vertices,Edges,Neighbours,Cyclic} = D, % get the table Id of the 3 tables containing D values
% It should be preferable to use the record definition of the digraph module
%-record(digraph, {vtab = notable :: ets:tab(),
%         etab = notable :: ets:tab(),
%         ntab = notable :: ets:tab(),
%             cyclic = true  :: boolean()}).
LV = ets:tab2list(Vertices),
LE = ets:tab2list(Edges),
LN = ets:tab2list(Neighbours),
...
% then serialize and send all this variables to the target node, ideally in one single tuple like 
% {my_digraph_data,LV,LE,LN,Cyclic} or using refs to avoid the mix of messages, 
% and on reception on the remote node:

receive
    {my_digraph_data,LV,LE,LN,Cyclic} ->
        Dcopy = digrapgh:new(Cyclic),
        {digraph,Vertices,Edges,Neighbours,_Cyclic} = Dcopy,
        ets:insert(Vertices,LV),
        ets:insert(Edges,LE),
        ets:insert(Neighbours,LN),
        Dcopy;
...

就是这样。

注意:如果您在相同的shell中测试此操作,请确保在派生进程的receive表达式中更改VerticesEdgesNeighbours的名称,否则它将失败并显示badmatch(因为在匹配D时它们已经绑定)。

相关问题