从复杂的元组/列表结构创建有效的函数声明

时间:2011-06-14 19:51:03

标签: erlang

在Erlang中给出一个复杂的对象,是否有一种通用的方法来为它提供一个有效的函数声明呢?我正在维护一些以前由巨型结构的忠实粉丝撰写的代码,并且证明它很容易出错。 我不需要迭代整个事情,只需抓住最高级别。

例如,我现在正在研究这个问题 -

[[["SIP",47,"2",46,"0"],32,"407",32,"Proxy Authentication Required","\r\n"],
 [{'Via',
        [{'via-parm',
              {'sent-protocol',"SIP","2.0","UDP"},
              {'sent-by',"172.20.10.5","5060"},
              [{'via-branch',"z9hG4bKb561e4f03a40c4439ba375b2ac3c9f91.0"}]}]},
  {'Via',
        [{'via-parm',
              {'sent-protocol',"SIP","2.0","UDP"},
              {'sent-by',"172.20.10.15","5060"},
              [{'via-branch',"12dee0b2f48309f40b7857b9c73be9ac"}]}]},
  {'From',
        {'from-spec',
             {'name-addr',
                  [[]],
                  {'SIP-URI',
                        [{userinfo,{user,"003018CFE4EF"},[]}],
                        {hostport,"172.20.10.11",[]},
                        {'uri-parameters',[]},
                        []}},
             [{tag,"b7226ffa86c46af7bf6e32969ad16940"}]}},
  {'To',
        {'name-addr',
             [[]],
             {'SIP-URI',
                  [{userinfo,{user,"3966"},[]}],
                  {hostport,"172.20.10.11",[]},
                  {'uri-parameters',[]},
                  []}},
        [{tag,"a830c764"}]},
  {'Call-ID',"90df0e4968c9a4545a009b1adf268605@172.20.10.15"},
  {'CSeq',1358286,"SUBSCRIBE"},
  ["date",'HCOLON',
    ["Mon",44,32,["13",32,"Jun",32,"2011"],32,["17",58,"03",58,"55"],32,"GMT"]],
  {'Contact',
        [[{'name-addr',
                [[]],
                {'SIP-URI',
                     [{userinfo,{user,"3ComCallProcessor"},[]}],
                     {hostport,"172.20.10.11",[]},
                     {'uri-parameters',[]},
                     []}},
          []],
         []]},
  ["expires",'HCOLON',3600],
  ["user-agent",'HCOLON',
    ["3Com",[]],
    [['LWS',["VCX",[]]],
     ['LWS',["7210",[]]],
     ['LWS',["IP",[]]],
     ['LWS',["CallProcessor",[['SLASH',"v10.0.8"]]]]]],
  ["proxy-authenticate",'HCOLON',
    ["Digest",'LWS',
     ["realm",'EQUAL',['SWS',34,"3Com",34]],
     [['COMMA',["domain",'EQUAL',['SWS',34,"3Com",34]]],
      ['COMMA',
        ["nonce",'EQUAL',
         ['SWS',34,"btbvbsbzbBbAbwbybvbxbCbtbzbubqbubsbqbtbsbqbtbxbCbxbsbybs",
          34]]],
      ['COMMA',["stale",'EQUAL',"FALSE"]],
      ['COMMA',["algorithm",'EQUAL',"MD5"]]]]],
  {'Content-Length',0}],
 "\r\n",
 ["\n"]]

5 个答案:

答案 0 :(得分:3)

答案 1 :(得分:1)

如果我理解正确,您希望模式匹配一​​些未知格式的大型数据结构。

示例:

Input: {a, b} {a,b,c,d} {a,[],{},{b,c}}

function({A, B}) -> do_something;
function({A, B, C, D}) when is_atom(B) -> do_something_else;
function({A, B, C, D}) when is_list(B) -> more_doing.

通用答案当然是仅仅知道如何对数据进行分类的数据是不可判定的。

首先,您应该了解iolists。它们由诸如io_lib:format / 2之类的函数以及代码中的许多其他位置创建。

一个例子是

 [["SIP",47,"2",46,"0"],32,"407",32,"Proxy Authentication Required","\r\n"]

将打印为

 SIP/2.0 407 Proxy Authentication Required

所以,我首先使用诸如

之类的函数展平所有这些列表
 flatten_io(List) when is_list(List) ->
     Flat = lists:map(fun flatten_io/1, List),
     maybe_flatten(Flat);
 flatten_io(Tuple) when is_tuple(Tuple) ->
      list_to_tuple([flatten_io(Element) || Element <- tuple_to_list(Tuple)];
 flatten_io(Other) -> Other.

 maybe_flatten(L) when is_list(L) ->
      case lists:all(fun(Ch) when Ch > 0 andalso Ch < 256 -> true;
                   (List) when is_list(List) ->
                        lists:all(fun(X) -> X > 0 andalso X < 256 end, List);
                   (_) -> false
                end, L) of
          true -> lists:flatten(L);
          false -> L
      end.

(警告:完全未经测试且非常低效。对于不合适的列表也会崩溃,但无论如何都不应该在数据结构中使用。)

第二个想法,我帮不了你。

应该取出并使用原子'COMMA'作为字符串中逗号的任何数据结构。

你也应该能够扁平化这些东西,并开始了解你所看到的东西。

我知道这不是一个完整的答案。希望它有所帮助。

答案 2 :(得分:1)

很难推荐处理这个问题。

以更合理,更简洁的格式转换所有结构看起来是值得的。这主要取决于这些结构的相似之处。

不必为100中的每一个都设置特殊功能,必须进行一些自动重新格式化,甚至可以将这些部分放在记录中。

一旦有了记录,就可以更容易地为它编写函数,因为您不需要知道记录中的实际元素数。更重要的是:当元素数量发生变化时,代码不会中断。

总结一下:通过以某种方式使用最通用的代码对它们进行消毒,在代码和这些结构的疯狂之间制造障碍。它可能是通用重新格式化与结构特定事物的混合。

作为此结构中已经可见的示例:'name-addr'元组看起来像是具有统一的结构。因此,您可以对结构(在元组和列表的所有元素上)进行递归,并匹配具有'name-addr'等公共结构的“事物”,并将其替换为不错的记录。

为了帮助你注意眼球,你可以在这个例子中写自己的帮助函数:

eyeball(List) when is_list(List) ->
    io:format("List with length ~b\n", [length(List)]);
eyeball(Tuple) when is_tuple(Tuple) ->
    io:format("Tuple with ~b elements\n", [tuple_size(Tuple)]).

所以你会得到这样的输出:

2> eyeball({a,b,c}).
Tuple with 3 elements
ok
3> eyeball([a,b,c]).
List with length 3
ok

将此扩展为一个有用的工具供您使用,留作练习。您可以通过递归元素并缩进输出来处理多个级别。

答案 3 :(得分:1)

我注意到你的澄清评论。我更愿意自己添加评论,但没有足够的业力。无论如何,我使用的技巧是在shell中进行实验。我会针对示例数据结构迭代一个模式,直到找到最简单的形式。您可以使用_ match-all变量。我在emacs shell窗口中使用了一个erlang shell。

首先,将样本绑定到变量:

  

A = [{a,b},[{c,d},{e,f}]]。

现在根据变量设置原始结构:

  

[{a,b},[{c,d},{e,f}]] = A。

如果按Enter键,您会看到它们匹配。点击alt-p(忘记emacs调用alt,但它在我的键盘上是alt)以恢复上一行。用下划线替换一些元组或列表项:

  

[_,[{C,d},{E,F}]]。

按Enter键以确保您做得正确且仍然匹配。这个例子很简单,但是对于深度嵌套的多线结构来说它更棘手,所以能够快速匹配测试很方便。有时你会想要尝试猜测整个巨大的区域,例如使用下划线来匹配元组内的元组列表,这是列表的第三个元素。如果你把它放好,你可以立刻匹配整个事物,但很容易误读它。

无论如何,重复探索结构的基本形状,并将实数变量放在想要拉出值的位置:

  

[_,[_,_]] = A。

     

[_,_] = A。

     

[_,MyTupleList] = A. %%让我们抓住这个元组列表

     

[{MyAtom,b},[{c,d},MyTuple]] = A. %%或者我们想要这个原子和元组

这就是我如何有效地剖析和模式匹配复杂的数据结构。

然而,我不知道你在做什么。我倾向于使用一个包装器函数,它使用KVC准确地提取你需要的东西,然后从那里为每种类型的结构分配给辅助函数。

答案 4 :(得分:0)

使用模式匹配和在列表上工作的函数来仅提取您需要的内容。

看看http://www.erlang.org/doc/man/lists.htmlkeyfindkeyreplaceL = [H|T],...