Damjan Cvetko

Damjan Cvetko

Developer, System Architect, Hacker.

2 minutes read

So I’m writing an Elixir parser that takes a << binary >> and converts it into a structure. Part of the protocol is such that the same parsing repeats a number of times, based on a previous field. In a non-functional language like GO, a simple for loop would be the best choice (parse and consume)…

Here are 3 ways I implemented this.

Loop with function and pattern matching

This is the first one I could think of. We define a function and use pattern matching on arguments to break out of recursion. The first defined variant has 0 as its repeat couinter. The second actually does the consuming. First taking 32 bits from rest and then recursing into itself by adding to the units list. Remember Elixir tail recursion and tail-call optimisation!

defp parse_unit(0, units, << rest:: binary >>) do
  {:ok, units, rest}
end
defp parse_unit(repeat, units, << rest:: binary >>) do
  << u :: 32, rest::binary >> = rest
  parse_unit(repeat - 1 , units ++ [u], rest)
end

We use this helper function like this. It will do num repeats, fill the empty list and consume rest.

{:ok, units, rest} = parse_unit(num, [], rest)

Using anonymous functions

The same thing, but defining the function locally as an anonymous function. Uglyer, but its close to the code where used.

parse_unit2 = fn
  _, 0, units, << rest:: binary >> -> {:ok, units, rest}
  f, repeat, units, << rest :: binary >> ->
    << u :: 32, rest::binary>> = rest
    f.(f, repeat - 1 , units ++ [u], rest)
end

{:ok, units, rest} = parse_unit2.(parse_unit2, num, [], rest)

Using Enum.reduce

Then I tried one of the Enum functions. Here I use the generated list 1..num to releat and the accumulator of Enum.reduce/3 to carry the consumable « binary » and the result.

{units, rest} = Enum.reduce(1..num, {[], rest}, fn _, {units, rest} ->
  << u :: 32, rest::binary>> = rest
  { units ++ [u], rest }
end)

Elixir is nuts. I like Elixir.

Recent posts

See more

Categories

About