【问题标题】:How to use erlang fun recursive function?如何使用erlang 有趣的递归函数?
【发布时间】:2019-04-11 17:15:50
【问题描述】:

我正在尝试解码具有可变长度和选项的帧(例如以太网帧中的 TLV) 为了做到这一点,我正在考虑做一个有趣的递归函数:

fun (Fields, Bin) ->
    Parse =
        fun (P, F, <<Length, Rest/binary>>) ->
                P(P, F#{first => Length}, Rest)
        end,
    Parse(Parse, Fields, Bin)
end.

所以 Bin 是输入帧,例如:40 02 12 45 01 50 所以第一个字节是帧的类型,02是后面数据的长度12 4501是后面数据的长度50等等。

但是我的函数使用这个有趣的技巧并没有像预期的那样工作

我正在返回 JSON 对象,因为它是通过 MQTT 发送的。

【问题讨论】:

    标签: recursion erlang frame


    【解决方案1】:

    你在那里所做的实际上只是读出长度,而不是对Rest 做太多事情。您需要首先声明一个退出条件,即当 TLV 为空时 -> 只需返回累加器;并使用模式匹配读取基于Length的值:

    parse(<<>>, Acc) -> Acc;  %% finished with the list
    parse(<<Length, Rest/binary>>, Acc) ->
       <<Value:Length/binary, Carry/binary>> = Rest.
       %% Value for the tag, Carry to be passed back on the recursion.
       %% Assuming that `Acc` is a list of Values.
       parse(Carry, Acc ++ [Value]).
    

    您可以使用上面的方法来读取值,并且可以执行类似的操作来首先获取类型:

    tlv(<<Type, Values/binary>>) ->
       %% Return at tuple with the Type and the values.
       {Type, parse(Values, [])}.
    

    【讨论】:

      【解决方案2】:

      你可以这样写函数:

      fun(<<Type, Packet/binary>>) ->
              {Type,
               fun Parse(<<>>) ->
                       [];
                   Parse(<<Length, Data:Length/bytes, Rest/binary>>) ->
                       [Data] ++ Parse(Rest)
               end(Packet)}
      end.
      

      这将为您的示例数据返回 {40,[&lt;&lt;12,45&gt;&gt;,&lt;&lt;50&gt;&gt;]}

      outer fun 采用帧类型(在本例中为 40),并将其与数据字段列表一起返回。内部 fun 获取一个长度字节和相应数量的数据字节,并返回数据并对自身进行递归调用 - 直到它到达二进制文件的末尾。

      内在乐趣是一种“命名乐趣”:它称自己为Parse,因此无需将自己作为参数传递即可调用自己。 Parse 这个名字在乐趣之外是不可见的。有关详细信息和示例,请参阅this question

      【讨论】:

        猜你喜欢
        • 2015-03-17
        • 2010-10-26
        • 2015-07-04
        • 1970-01-01
        • 1970-01-01
        • 2019-10-24
        • 1970-01-01
        • 2018-04-03
        • 1970-01-01
        相关资源
        最近更新 更多