Permalink
Cannot retrieve contributors at this time
defmodule PexQueue do | |
@moduledoc """ | |
A Queue that uses the First In, First Out (FIFO) method, | |
unless provided with a function to prioritise items. | |
An example of using the prioritise function might be to order | |
from highest to lowest numbers, regardless of when they are added to the queue. | |
For example, we have this queue: | |
```elixir | |
{:ok, pid} = PexQueue.start_link(fn first, second -> first < second end) | |
PexQueue.enqueue(pid, 2) | |
PexQueue.enqueue(pid, 7) | |
PexQueue.enqueue(pid, 5) | |
``` | |
**Queue state:** `[7, 5, 2]` | |
When we add 6 to the queue, it results in: | |
```elixir | |
PexQueue.enqueue(pid, 6) | |
``` | |
**Queue state:** `[7, 6, 5, 2]` | |
This is in contrast to a queue with a provided prioritisation function, | |
which would return items in the order they were added. | |
```elixir | |
{:ok, pid} = PexQueue.start_link() | |
PexQueue.enqueue(pid, 2) | |
PexQueue.enqueue(pid, 7) | |
PexQueue.enqueue(pid, 5) | |
# Queue state: [2, 5, 7] | |
PexQueue.dequeue(pid) # 2 | |
PexQueue.dequeue(pid) # 5 | |
PexQueue.dequeue(pid) # 7 | |
``` | |
""" | |
use GenServer | |
@doc """ | |
Adds an item to the queue | |
""" | |
def enqueue(pid, item) do | |
GenServer.call(pid, {:enqueue, item}) | |
end | |
@doc """ | |
Removes an item from the queue | |
""" | |
def dequeue(pid) do | |
GenServer.call(pid, :dequeue) | |
end | |
@doc """ | |
Returns the first item in the queue, without altering the queue state | |
""" | |
@spec peek(pid()) :: any() | |
def peek(pid) do | |
GenServer.call(pid, :peek) | |
end | |
@doc """ | |
Returns the length of the queue | |
""" | |
@spec count(pid()) :: integer() | |
def count(pid) do | |
GenServer.call(pid, :count) | |
end | |
@doc """ | |
Start a new Priority Queue | |
""" | |
@spec start_link(fun()) :: {:ok, pid()} | |
def start_link(prioritise \\ fn _, _ -> true end) do | |
GenServer.start_link(__MODULE__, %{queue: [], prioritise: prioritise}) | |
end | |
@doc false | |
def init(args) do | |
{:ok, args} | |
end | |
def handle_call(:dequeue, _from, %{queue: []} = state), do: {:reply, nil, state} | |
def handle_call(:dequeue, _from, %{queue: q} = state) do | |
[h | tail] = Enum.reverse(q) | |
queue = Enum.reverse(tail) | |
{:reply, h, add_queue_to_state(queue, state)} | |
end | |
def handle_call({:enqueue, item}, _from, %{queue: q, prioritise: p} = state) do | |
updated_queue = Enum.sort_by([item | q], fn i -> i end, p) | |
{:reply, updated_queue, add_queue_to_state(updated_queue, state)} | |
end | |
def handle_call(:peek, _from, %{queue: q} = state) do | |
[h | _] = Enum.reverse(q) | |
{:reply, h, state} | |
end | |
def handle_call(:count, _from, %{queue: q} = state) do | |
{:reply, length(q), state} | |
end | |
defp add_queue_to_state(queue, state) do | |
Map.merge(state, %{queue: queue}) | |
end | |
end |