Botonomous
Botonomous

Reputation: 1764

Elixir Queue (Erlang :queue) - Enum.Take

Going through the docs of the erlang queue here: http://erlang.org/doc/man/queue.html#member-2 I don't see a way to pull off a range of items like Enum.Take. Has anyone solved this?

Upvotes: 0

Views: 713

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Erlang is proud of using recursion wherever possible instead of imperative calls. The desired behaviour might be easily implemented:

def take(q, amount), do: do_take(q, {amount, []})
defp do_take(q, {n, acc}) when n > 0 do
  case :queue.out(q) do
    {{:value, e}, rest} ->
      do_take(rest, {n - 1, [e | acc]})
    {:empty, q} -> {acc, q}
  end
end
defp do_take(q, {_, acc}), do: {acc, q}

I have not tested this code, but I believe the idea is clear.


Or, with a fancy else syntax:

def take(q, amount), do: do_take(q, {amount, []})
defp do_take(q, {n, acc}) when n > 0 do
  :queue.out(q)
else
  {{:value, e}, rest} -> do_take(rest, {n - 1, [e | acc]})
  {:empty, q} -> {acc, q}
end
defp do_take(q, {_, acc}), do: {acc, q}

Upvotes: 2

7stud
7stud

Reputation: 48599

I don't see a way to pull off a range of items like Enum.Take. Has anyone solved this?

Yep. From the page you linked:

split(N :: integer() >= 0, Q1 :: queue(Item)) ->
         {Q2 :: queue(Item), Q3 :: queue(Item)}

Splits Q1 in two. The N front items are put in Q2 and the rest in Q3.

So, you can do this:

-module(my).
-compile(export_all).

take(N, Q) ->
    {Taken, _Rest} = queue:split(N, Q),
    Taken.

In the shell:

1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> Q = queue:from_list([1,2,3,4]).
{[4],[1,2,3]}

3> T1 = my:take(1, Q).
{[],[1]}

4> queue:to_list(T1). 
[1]

5> T3 = my:take(3, Q).
{[3],[1,2]}

6> queue:to_list(T3). 
[1,2,3]

All operations have an amortized O(1) running time, except filter/2, join/2, len/1, member/2, split/2 that have O(n).

Upvotes: 1

Related Questions