在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"]]
答案 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.html:
keyfind
,keyreplace
,L = [H|T]
,...