psq v0.1.0 PSQ

PSQ provides a purely-functional implementation of priority search queues. A priority search queue is a data structure that efficiently supports both associative operations (like those for Map) and priority-queue operations (akin to heaps in imperative languages). The implementation is based on the Haskell PSQueue package and the associated paper.

PSQs can be created from lists in O(n log n) time. Once created, the minimum element (min/1 and size (Enum.count/1) can be accessed in O(1) time; most other basic operations (including get/2, pop/1, and put/2, and delete/2 are in O(log n).

PSQs implement Enumerable and Collectable, so all your favorite functions from Enum and Stream should work as expected.

Each entry in a PSQ has an associated priority and key. Map-like operations, such as get/2, use keys to find the entry; all entries in a PSQ are unique by key. Ordered operations, such as pop/1 and Enum.to_list/1, use priority to determine order (with minimum first). Priorities need not be unique by entry; entries with the same priority will be popped in unspecified order.

Examples

There are two primary ways to determine a value’s priority and key in a queue. The simplest is to start with an empty queue and input values with priorities and keys directly, through put/4:

iex> q = PSQ.new |> PSQ.put(:a, "foo", 2) |> PSQ.put(:b, "bar", 1)
iex> q |> PSQ.get(:a)
"foo"
iex> q |> PSQ.min
"bar"

Alternatively, you can specify mapper functions to determine key and priority for all entries in the queue. This is particularly useful for determining custom priorities. For example, here’s a simple method to use PSQs for max-queues:

iex> q = PSQ.new(&(-&1))
iex> q = [?a, ?b, ?c, ?d, ?e] |> Enum.into(q)
iex> q |> Enum.to_list
[?e, ?d, ?c, ?b, ?a]

Here’s a queue that orders strings by size, using downcased strings as keys:

iex> q = PSQ.new(&String.length/1, &String.downcase/1)
iex> q = ["How", "is", "your", "ocelot"] |> Enum.into(q)
iex> q |> Enum.to_list
["is", "How", "your", "ocelot"]
iex> q |> PSQ.get("how")
"How"
iex> q |> PSQ.get("How")
nil

Priority and key mappers are also useful if you’re inputting entries that are structs or maps and want to use particular fields as keys or priorities. For example:

iex> q = PSQ.new(&(&1[:priority]), &(&1[:key]))
iex> q = PSQ.put(q, %{priority: 5, key: 1})
iex> q = PSQ.put(q, %{priority: 2, key: 2})
iex> q = PSQ.put(q, %{priority: 1, key: 1})
iex> q |> PSQ.min
%{priority: 1, key: 1}
iex> q |> PSQ.get(1)
%{priority: 1, key: 1}

Summary

Functions

Returns a list of all values from q where the value’s priority is less than or equal to priority

Deletes the value associated with key from q

Fetches the value for specified key and returns in a tuple. Returns :error if the key does not exist

Fetches the value for specified key

Gets the value for specified key. If the key does not exist, returns nil

Returns the value with the minimum priority from q

Returns and removes the value with the minimum priority from q. The value will be nil if the queue is empty

Puts the given value into the queue, using priority_mapper and key_mapper to determine uniqueness/order (see new)

Puts the given value into the queue with specified key and priority

Types

key :: any
key_mapper :: (value -> key)
priority :: any
t :: %PSQ{key_mapper: key_mapper, priority_mapper: priority_mapper, tree: PSQ.Winner.t}
value :: any

Functions

at_most(q, priority)

Specs

at_most(t, priority) :: [value]

Returns a list of all values from q where the value’s priority is less than or equal to priority.

Examples

iex> PSQ.from_list([1, 3, 2, 5, 4]) |> PSQ.at_most(3)
[1, 2, 3]
delete(q, key)

Specs

delete(t, key) :: t

Deletes the value associated with key from q.

If key does not exist, returns q unchanged.

Examples

iex> PSQ.from_list([3,1,2]) |> PSQ.delete(2) |> Enum.to_list
[1, 3]
iex> PSQ.from_list([3,1,2]) |> PSQ.delete(4) |> Enum.to_list
[1, 2, 3]
fetch(q, key)

Specs

fetch(t, key) :: {:ok, value} | :error

Fetches the value for specified key and returns in a tuple. Returns :error if the key does not exist.

Examples

iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.fetch(:a)
{:ok, 3}
iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.fetch(:b)
:error
fetch!(q, key)

Specs

fetch!(t, key) :: value | no_return

Fetches the value for specified key.

If key does not exist, a KeyError is raised.

Examples

iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.fetch!(:a)
3
iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.fetch!(:b)
** (KeyError) key :b not found in: #PSQ<min:3 size:1>
from_list(list, priority_mapper \\ &(&1), key_mapper \\ &(&1))

Specs

from_list([value], priority_mapper, key_mapper) :: t

Returns a new PSQ from list.

priority_mapper and key_mapper behave the same way as for new/2.

Examples

iex> [2, 5, 4, 1, 3] |> PSQ.from_list |> Enum.to_list
[1, 2, 3, 4, 5]
get(q, key)

Specs

get(t, key) :: value

Gets the value for specified key. If the key does not exist, returns nil.

Examples

iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.get(:a)
3
iex> PSQ.new |> PSQ.put(:a, 3, 1) |> PSQ.get(:b)
nil
min(q)

Specs

min(t) :: value | no_return

Returns the value with the minimum priority from q.

Raises Enum.EmptyError if the queue is empty.

Examples

iex> PSQ.from_list([-2, 3, -5]) |> PSQ.min
-5
iex> PSQ.from_list([-2, 3, -5], &(-&1)) |> PSQ.min
3
iex> PSQ.new |> PSQ.min
** (Enum.EmptyError) empty error
new(priority_mapper \\ &(&1), key_mapper \\ &(&1))

Specs

Returns a new empty PSQ.

Optional params priority_mapper and key_mapper are functions to determine keys and priorities from values. For example, to create a max-queue of numbers instead of a min-queue, pass in &(-&1) for priority_mapper:

iex> PSQ.new(&(-&1)) |> PSQ.put(3) |> PSQ.put(5) |> PSQ.put(1) |> Enum.to_list
[5, 3, 1]

key_mapper is useful if your values are structs where particular fields are considered a unique key:

iex> q = PSQ.new(&(&1[:priority]), &(&1[:key]))
iex> q = q |> PSQ.put(%{key: 1, priority: 1})
iex> q = q |> PSQ.put(%{key: 1, priority: 3})
iex> q |> PSQ.get(1)
%{key: 1, priority: 3}

priority_mapper and key_mapper both default to the identity function.

pop(q)

Specs

pop(t) :: {value, t}

Returns and removes the value with the minimum priority from q. The value will be nil if the queue is empty.

Examples

iex> q = PSQ.from_list([3, 1])
iex> {min, q} = PSQ.pop(q)
iex> min
1
iex> {min, q} = PSQ.pop(q)
iex> min
3
iex> {min, q} = PSQ.pop(q)
iex> min
nil
iex> Enum.empty?(q)
true
put(q, value)

Specs

put(t, value) :: t

Puts the given value into the queue, using priority_mapper and key_mapper to determine uniqueness/order (see new).

If a value with the same key already exits in the queue, it will be replaced by the new value.

Examples

iex> q = PSQ.new(&(&1), &trunc/1)
iex> q = PSQ.put(q, 3.89)
iex> q = PSQ.put(q, 2.71)
iex> q = PSQ.put(q, 3.14)
iex> Enum.to_list(q)
[2.71, 3.14]
put(q, key, val, priority)

Specs

put(t, key, value, priority) :: t

Puts the given value into the queue with specified key and priority.

When using this function (as opposed to put/2, the queue’s priority_mapper and key_mapper will be ignored. It is not recommended to use both mappers and direct keys/priorities for the same queue.

Examples

iex> PSQ.new |> PSQ.put(:a, 1, 1) |> PSQ.put(:a, 2, 1) |> PSQ.get(:a)
2

iex> PSQ.new |> PSQ.put(:a, 1, 2) |> PSQ.put(:b, 2, 1) |> Enum.to_list
[2, 1]