Как использовать erlang fun рекурсивную функцию? - PullRequest
1 голос
/ 11 апреля 2019

Я пытаюсь декодировать кадры с переменной длиной и параметрами (например, TLV в кадрах Ethernet) Для этого я подумал о том, чтобы сделать забавную рекурсивную функцию:

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 45, 01 - длина следующих данных 50 и т. Д.

Но моя функция не работает должным образом, используя забавный трюк

Я возвращаю объект JSON, поскольку он отправляется через MQTT.

Ответы [ 2 ]

1 голос
/ 11 апреля 2019

То, что вы делаете там, на самом деле только считывает длину и мало что делает с 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, [])}.
0 голосов
/ 11 апреля 2019

Вы можете написать функцию следующим образом:

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

Возвращает {40,[<<12,45>>,<<50>>]} для данных образца.

Внешняя забава берет тип фрейма (в нашем случае 40) и возвращает его вместе со списком полей данных. Внутренняя забава берет один байт длины и соответствующее количество байтов данных, возвращает данные и делает рекурсивный вызов самому себе - пока он не достигнет конца двоичного файла.

Внутреннее веселье - это «именное веселье»: оно называет себя Parse и поэтому может вызывать себя, не передавая себя в качестве аргумента. Имя Parse не видно вне веселья. См. этот вопрос для деталей и примеров.

...