【问题标题】:Creating a valid function declaration from a complex tuple/list structure从复杂的元组/列表结构创建有效的函数声明
【发布时间】:2011-06-16 07:35:10
【问题描述】:

在给定 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"]]

【问题讨论】:

  • 我很乐意回答。我只是不知道实际的问题是什么。 “有效的函数声明”是什么意思?你的意思是生成数据结构的东西,还是可以读取数据结构的东西?或者别的什么。
  • 抱歉,措辞不明确 - 本质上,假设您有一个 3 元组,看起来像这样 { 4, blah, [] }。您可以通过查看它来简单地推导出,您需要一个如下所示的函数: myfunc( { A, B, C } ) 。如果你有一个大的、嵌套的元组/列表——甚至更好,很多,并且你需要创建作用于每一个的函数,那么找出每个变体顶层的“形状”是非常耗时的.
  • 我也很疑惑。阅读还是创作?通用代码或一些帮助生成代码,例如功能。或者,也许只是目测的一些帮手。而且我不得不这么说,以保护无辜者这些看起来像一些丑陋的结构,不要在家里这样做!

标签: erlang


【解决方案1】:

【讨论】:

  • 这非常酷,所以我正在修改它,尽管它没有回答问题:)
【解决方案2】:

我注意到您的澄清评论。我更愿意自己添加评论,但没有足够的业力。无论如何,我使用的技巧是在 shell 中进行实验。我将针对示例数据结构迭代模式,直到找到最简单的形式。您可以使用 _ 匹配所有变量。我在 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 来准确提取您需要的内容,然后从那里为每种类型的结构分发到辅助函数。

【讨论】:

  • 我认为这基本上是您可能希望提出的最有效的过程,而不是编写一些代码来为您创建函数定义,我想我应该将其标记为答案,尽管有这里的其他一些聪明的答案值得称赞。
【解决方案3】:

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

例子:

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'作为字符串中逗号的数据结构都应该被取出并删除。

您也应该能够将这些东西展平,并开始了解您正在查看的内容。

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

【讨论】:

  • +1 for:“任何在字符串中使用原子'COMMA'作为逗号的数据结构都应该被取出并删除。”
【解决方案4】:

很难推荐一些东西来处理这个问题。

将所有结构转换为更合理且更简洁的格式看起来是值得的。这主要取决于这些结构的相似性。

与其为这 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

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

【讨论】:

    【解决方案5】:

    使用适用于列表的模式匹配和函数来仅提取您需要的内容。

    看http://www.erlang.org/doc/man/lists.html: keyfind, keyreplace, L = [H|T], ...

    【讨论】:

    • 嗯,其中一些在树的顶层没有列表,它可能是一个包含 8,9...12 个值或其他东西的元组。根据不同的结构,我有大约 100 个要制作。我确信有一种更简单的方法可以做到这一点,但我确实需要基于它们的形式单独的逻辑,所以我目前不知道有一种方法可以将它抽象出来。
    • 据我所知,对于元组,只有模式匹配。还有,记录。
    猜你喜欢
    • 2014-04-14
    • 2020-09-23
    • 2020-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    相关资源
    最近更新 更多