Packages

An Elixir wrapper around the Erlang optimized `queue` that supports the FIFO, first-in first-out, pattern. This is useful is when you can't predict when an item needs to be taken or added to the queue. Use this instead of using `++` or double reversing lists to add items to the "back" of a queue.

Current section

Files

Jump to
e_queue lib e_queue.ex
Raw

lib/e_queue.ex

defmodule EQueue do
@moduledoc """
A simple wrapper around the Erlang Queue library that follows the idiomatic
pattern of expecting the module target first to take advantage of the pipeline
operator.
Queues are double ended. The mental picture of a queue is a line of people
(items) waiting for their turn. The queue front is the end with the item that
has waited the longest. The queue rear is the end an item enters when it
starts to wait. If instead using the mental picture of a list, the front is
called head and the rear is called tail.
Entering at the front and exiting at the rear are reverse operations on the queue.
"""
defstruct data: :queue.new
@type t :: %EQueue{data: {[any],[any]} }
@doc """
Returns an empty queue
== Example
iex> EQueue.new
#EQueue<[]>
"""
@spec new :: EQueue.t
def new(), do: %EQueue{}
@doc """
Calculates and returns the length of given queue
== Example
iex> EQueue.from_list([:a, :b, :c]) |> EQueue.length
3
"""
@spec length(EQueue.t) :: pos_integer()
def length(%EQueue{data: queue}), do: :queue.len(queue)
@doc """
Adds an item to the end of the queue, returns the resulting queue
== Example
iex> EQueue.new |> EQueue.push(:a)
#EQueue<[:a]>
"""
@spec push(EQueue.t, any) :: EQueue.t
def push(%EQueue{data: queue}, item), do: :queue.in(item, queue) |> wrap
@doc """
Removes the item at the front of queue. Returns the tuple {:value, item, Q2},
where item is the item removed and Q2 is the resulting queue. If Q1 is empty,
the tuple {:empty, Q1} is returned.
== Example
iex> EQueue.from_list([:a, :b]) |> EQueue.pop
{:value, :a, %EQueue{data: {[], [:b]} }}
iex> EQueue.new |> EQueue.pop
{:empty, EQueue.new}
"""
@spec pop(EQueue.t) :: {:value, any, EQueue.t}
| {:empty, EQueue.t}
def pop(%EQueue{data: queue}) do
case :queue.out(queue) do
{{:value, value}, new_queue} -> {:value, value, wrap(new_queue)}
{:empty, ^queue} -> {:empty, wrap(queue)}
end
end
@doc """
Returns a list of the items in the queue in the same order;
the front item of the queue will become the head of the list.
== Example
iex> EQueue.from_list([1, 2, 3, 4, 5]) |> EQueue.to_list
[1, 2, 3, 4, 5]
"""
@spec to_list(EQueue.t) :: [any]
def to_list(%EQueue{data: queue}), do: :queue.to_list(queue)
@doc """
Returns a queue containing the items in L in the same order;
the head item of the list will become the front item of the queue.
== Example
iex> EQueue.from_list [1, 2, 3, 4, 5]
#EQueue<[1, 2, 3, 4, 5]>
"""
@spec from_list([any]) :: EQueue.t
def from_list(list), do: :queue.from_list(list) |> wrap
@doc """
Returns a new queue with the items for the given queue in reverse order
== Example
iex> EQueue.from_list([1, 2, 3, 4, 5]) |> EQueue.reverse
#EQueue<[5, 4, 3, 2, 1]>
"""
@spec reverse(EQueue.t) :: EQueue.t
def reverse(%EQueue{data: queue}), do: :queue.reverse(queue) |> wrap
@doc """
With a given queue and an amount it returns {Q2, Q3}, where Q2 contains
the amount given and Q2 holds the rest. If attempted to split an empty
queue or past the length an argument error is raised
== Example
iex> EQueue.from_list([1, 2, 3, 4, 5]) |> EQueue.split(3)
{EQueue.from_list([1,2,3]), EQueue.from_list([4,5])}
iex> EQueue.from_list([1, 2, 3, 4, 5]) |> EQueue.split(12)
** (ArgumentError) argument error
"""
@spec split(EQueue.t, pos_integer()) :: {EQueue.t, EQueue.t}
def split(%EQueue{data: queue}, amount) do
{left, right} = :queue.split(amount, queue)
{wrap(left), wrap(right)}
end
@doc """
Given two queues, an new queue is returned with the second appended to
the end of the first queue given
== Example
iex> EQueue.from_list([1]) |> EQueue.join(EQueue.from_list([2]))
#EQueue<[1, 2]>
"""
@spec join(EQueue.t, EQueue.t) :: EQueue.t
def join(%EQueue{data: front}, %EQueue{data: back}) do
:queue.join(front, back) |> wrap
end
@doc """
With a given queue and function, a new queue is returned in the same
order as the one given where the function returns true for an element
== Example
iex> EQueue.from_list([1, 2, 3, 4, 5]) |> EQueue.filter(fn x -> rem(x, 2) == 0 end)
#EQueue<[2, 4]>
"""
@spec filter(EQueue.t, Fun) :: EQueue.t
def filter(%EQueue{data: queue}, fun), do: :queue.filter(fun, queue) |> wrap
@doc """
Returns true if the given element is in the queue, false otherwise
== Example
iex> EQueue.from_list([1, 2, 3]) |> EQueue.member? 2
true
iex> EQueue.from_list([1, 2, 3]) |> EQueue.member? 9
false
"""
@spec member?(EQueue.t, any) :: true | false
def member?(%EQueue{data: queue}, item), do: :queue.member(item, queue)
@doc """
Returns true if the given queue is empty, false otherwise
== Example
iex> EQueue.from_list([1, 2, 3]) |> EQueue.empty?
false
iex> EQueue.new |> EQueue.empty?
true
"""
@spec empty?(EQueue.t) :: true | false
def empty?(%EQueue{data: queue}), do: :queue.is_empty(queue)
@doc """
Returns true if the given item is a queue, false otherwise
iex> EQueue.new |> EQueue.is_queue?
true
iex> {:a_queue?, [], []} |> EQueue.is_queue?
false
"""
@spec is_queue?(any) :: true | false
def is_queue?(%EQueue{data: queue}), do: :queue.is_queue(queue)
def is_queue?(_), do: false
@doc false
@spec wrap({[any], [any]}) :: EQueue.t
defp wrap(data), do: %EQueue{data: data}
end