Current section
Files
Jump to
Current section
Files
lib/modules/queue_assistant.ex
defmodule QueueAssistant do
@moduledoc """
This is a simple wrapper for the Erlang queue, in which the order of some entries
has also been changed
- **Based on:** https://www.erlang.org/doc/man/queue
"""
@type queue_type :: :queue.queue(any())
@doc """
Returns true if all elements in enumerable are truthy. It is like `Enum.all?/1`.
### Example:
```elixir
new()
|> insert(1)
|> all?(fn x -> x >= 1 end)
```
"""
@spec all?((any() -> boolean()), queue_type()) :: boolean()
def all?(fun, queue) do
:queue.all(fun, queue)
end
@doc """
Returns true if at least one element in enumerable is truthy.
### Example:
```elixir
new()
|> insert(1)
|> insert(2)
|> any?(fn x -> x >= 2 end)
```
"""
@spec any?(queue_type(), (any() -> boolean())) :: boolean()
def any?(queue, fun) do
:queue.any(fun, queue)
end
@doc """
Returns a copy of `q1` where the first item matching `item` is deleted, if there is such an item.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = queue |> delete(3)
member(queue1, 3)
# false
```
"""
@spec delete(queue_type(), any()) :: queue_type()
def delete(queue, item) do
:queue.delete(item, queue)
end
@doc """
Returns a copy of `q1` where the last item matching `item` is deleted, if there is such an item.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 3, 5])
queue1 = delete_r(queue, 3)
to_list(queue1).
[1,2,3,4,5]
```
"""
@spec delete_r(queue_type(), any()) :: queue_type()
def delete_r(queue, item) do
:queue.delete_r(item, queue)
end
@doc """
Returns a copy of `q1` where the first item for which `fn` returns true is deleted,
if there is such an item.
### Example:
```elixir
queue = from_list([100,1, 2, 3, 4, 5])
queue1 = delete_with(queue, fn x -> x > 0)
to_list(queue1)
[1,2,3,4,5]
```
If we make it abstract in Elixir, you can do it with a list like this:
```elixir
list = [1, 2, 3, 4, 5]
{before, [h | t]} = Enum.split_with(list, fn x -> x < 4 end)
new_list = before ++ t
```
"""
@spec delete_with(queue_type(), (any() -> boolean())) :: queue_type()
def delete_with(queue, fun) do
:queue.delete_with(fun, queue)
end
@doc """
Returns a copy of `q1` where the last item for which `fn` returns true is deleted,
if there is such an item.
### Example:
```elixir
queue = from_list([100,1, 4, 2, 3, 4, 5])
queue1 = delete_with_r(queue, fn x -> x == 4)
to_list(queue1)
[100,1, 4, 2, 3, 5]
```
"""
@spec delete_with_r(queue_type(), (any() -> boolean())) :: queue_type()
def delete_with_r(queue, fun) do
:queue.delete_with_r(fun, queue)
end
@doc """
Filters the enumerable, i.e. returns only those elements for which fun returns a truthy value.
It is like `Enum.filter/2`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = filter(queue, fn x -> x > 2 end)
to_list(queue1)
# [3, 4, 5]
```
### From Erlang docs:
> So, `Fun(Item)` returning `[Item]` is thereby semantically equivalent to returning true,
> just as returning `[]` is semantically equivalent to returning false.
>
> But returning a list builds more garbage than returning an atom.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
# {[5, 4, 3], [1, 2]}
queue1 = filter(queue, fn x -> [x, x+1] end)
# {[6, 5, 5, 4, 4, 3], [1, 2, 2, 3]}
to_list(queue1)
# [1, 2, 2, 3, 3, 4, 4, 5, 5, 6]
```
"""
@spec filter(queue_type(), (any() -> boolean() | list())) :: queue_type()
def filter(queue, fun) do
:queue.filter(fun, queue)
end
@doc """
### From Erlang doc:
Returns a queue `q2` that is the result of calling `Fun(Item)` on all items in `q1`.
If `Fun(Item)` returns true, Item is copied to the result queue. If it returns **false**,
Item is not copied.
If it returns `{true, NewItem}`, the queue element at this position is replaced with `NewItem`
in the result queue.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = filtermap(queue, fn x -> x > 2 end)
to_list(queue1)
# [3, 4, 5]
```
"""
@spec filtermap(queue_type(), (any() -> boolean() | {true, any()})) ::
queue_type()
def filtermap(queue, fun) do
:queue.filtermap(fun, queue)
end
@doc """
Invokes fun for each element in the enumerable with the accumulator. It is like `Enum.reduce/3`.
### From Erlang docs:
Calls Fun(Item, AccIn) on successive items Item of Queue, starting with `AccIn == Acc`0.
The queue is traversed in queue order, that is, from front to rear.
`Fun/2` must return a new accumulator, which is passed to the next call.
The function returns the final value of the accumulator. `Acc0` is returned if the queue is empty.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
1> fold(queue, 0, fn item, acc -> item + acc end)
# 15
2> fold(queue, 0, fn item, acc -> item * acc end)
# 120
```
"""
@spec fold(queue_type(), any(), (any(), any() -> any())) :: any()
def fold(queue, acc, fun) do
:queue.fold(fun, acc, queue)
end
@doc """
For see more information, check `fold/3`
"""
@spec reduce(queue_type(), any(), (any(), any() -> any())) :: any()
def reduce(queue, acc, fun), do: fold(queue, acc, fun)
@doc """
Returns a queue containing the items in the same order; the head item of
the list becomes the front item of the queue.
### Example:
```elixir
from_list([1, 2, 3, 4, 5])
# {[5, 4, 3], [1, 2]}
```
"""
@spec from_list(list()) :: queue_type()
def from_list(items) do
:queue.from_list(items)
end
@doc """
Inserts Item at the rear of queue `q1`. Returns the resulting queue `q2`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = insert(queue, 100)
to_list(queue1)
# [1, 2, 3, 4, 5, 100]
```
"""
@spec insert(queue_type(), any()) :: queue_type()
def insert(queue, item) do
:queue.in(item, queue)
end
@doc """
Inserts Item at the front of queue `q1`. Returns the resulting queue `q2`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = insert_r(queue, 100)
to_list(queue1)
# [100, 1, 2, 3, 4, 5]
```
"""
@spec insert_r(queue_type(), any()) :: queue_type()
def insert_r(queue, item) do
:queue.in_r(item, queue)
end
@doc """
For see more information, check `insert_r/2`
"""
@spec in_r(queue_type(), any()) :: queue_type()
def in_r(queue, item), do: insert_r(queue, item)
@doc """
Tests if a queue is empty and returns true if so, otherwise false.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
is_empty?(queue)
# false
```
"""
@spec is_empty?(queue_type() | nil) :: boolean()
def is_empty?(nil), do: true
def is_empty?(queue) do
:queue.is_empty(queue)
end
@spec empty?(queue_type()) :: boolean()
@doc """
For see more information, check `is_empty?/1`
"""
@spec empty?(queue_type() | nil) :: boolean()
def empty?(queue), do: is_empty?(queue)
@doc """
Tests if an entry is queue and returns true if so, otherwise false.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
is_queue?(queue)
# true
```
"""
@spec is_queue?(queue_type()) :: boolean()
def is_queue?(queue) do
:queue.is_queue(queue)
end
@doc """
For see more information, check `is_queue?/1`
"""
@spec queue?(queue_type()) :: boolean()
def queue?(queue), do: is_queue?(queue)
@doc """
Returns a queue `q3` that is the result of joining `q1` and `q2` with `q1` in front of `q2`.
### Example:
```elixir
queue = from_list([1, 2])
queue1 = from_list([3, 4])
join(queue, queue1)
# [1, 2, 3, 4]
```
"""
@spec join(queue_type(), queue_type()) :: queue_type()
def join(queue, queue1) do
:queue.join(queue, queue1)
end
@doc """
Calculates and returns the length of a queue.
### Example:
```elixir
queue = from_list([1, 2])
len(queue)
# 2
```
"""
@spec len(queue_type()) :: non_neg_integer()
def len(queue) do
:queue.len(queue)
end
@doc """
For see more information, check `len/1`
"""
@spec count(queue_type()) :: non_neg_integer()
def count(queue), do: len(queue)
@doc """
Checks if element exists within the queue.
### Example:
```elixir
queue = from_list([1, 2])
member?(queue, 2)
# true
```
"""
@spec member?(queue_type(), any()) :: boolean()
def member?(queue, item) do
:queue.member(item, queue)
end
@doc """
Returns an empty queue.
### Example:
```elixir
new()
# {[], []}
```
"""
@spec new() :: queue_type()
def new() do
:queue.new()
end
@doc """
Removes the item at the front of queue `q1`. Returns tuple `{{:value, item}, q2}`,
where Item is the item removed and `q2` is the resulting queue.
If `q1` is empty, tuple `{empty, q1}` is returned.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
# {[5, 4, 3],[1, 2]}
{{:value, 1}, new_queue} = out(queue)
# {{:value, 1}, {[5, 4, 3], [2]}}
to_list(new_queue)
# [2, 3, 4, 5]
```
"""
@dialyzer {:nowarn_function, out: 1}
@spec out(queue_type()) :: {:empty, {[], []}} | {{:value, any()}, queue_type()}
def out(queue) do
:queue.out(queue)
end
@doc """
Removes the item at the rear of queue `q1`. Returns tuple `{{:value, item}, q2}`,
where Item is the item removed and `q2` is the resulting queue.
If `q1` is empty, tuple `{empty, q1}` is returned.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
# {[5,4,3], [1, 2]}
{{:value, 5}, new_queue} = out_r(queue)
# {{:value, 5}, {[5, 4, 3], [2, 1]}}
to_list(new_queue)
# [1, 2, 3, 4]
```
"""
@spec out_r(queue_type()) :: {:empty | {:value, any()}, queue_type()}
def out_r(queue) do
:queue.out_r(queue)
end
@doc """
Returns a queue `q2` containing the items of `q1` in the reverse order.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
reverse(queue)
# {[1, 2], [5, 4, 3]}
```
"""
@spec reverse(queue_type()) :: queue_type()
def reverse(queue) do
:queue.reverse(queue)
end
@doc """
Splits `q1` in two. The `n` front items are put in `q2` and the rest in `q3`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
split(queue, 2)
# {{[2], [1]}, {[5], [3, 4]}}
```
"""
@spec split(queue_type(), non_neg_integer()) ::
{queue_type(), queue_type()}
def split(queue, at) do
:queue.split(at, queue)
end
@doc """
Returns a list of the items in the queue in the same order; the front item of
the queue becomes the head of the list.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
# {[5, 4, 3], [1, 2]}
to_list(queue)
# [1, 2, 3, 4, 5]
```
"""
@spec to_list(queue_type()) :: list()
def to_list(queue) do
:queue.to_list(queue)
end
# Extended APIs
@doc """
Returns a queue `q2` that is the result of removing the front item from `q1`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = drop(queue)
to_list(queue1)
# [2,3,4,5]
```
> **Fails with reason empty if `q1` is empty.** You must check if it is empty before dropping it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:252: :queue.drop({[], []})
iex:55: (file)
```
"""
@spec drop(queue_type()) :: queue_type()
def drop(queue) do
:queue.drop(queue)
end
@doc """
Returns a queue `q2` that is the result of removing the rear item from `q1`.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
queue1 = drop_r(queue)
to_list(queue1)
# [1, 2, 3, 4]
```
> **Fails with reason empty if `q1` is empty.** You must check if it is empty before dropping it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:270: :queue.drop_r({[], []})
iex:58: (file)
```
"""
@spec drop_r(queue_type()) :: queue_type()
def drop_r(queue) do
:queue.drop_r(queue)
end
@doc """
Returns Item at the front of a queue.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
1 == get(queue)
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before getting it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:188: :queue.get({[], []})
iex:58: (file)
```
"""
@spec get(queue_type()) :: any()
def get(queue) do
:queue.get(queue)
end
@doc """
Returns Item at the rear of a queue.
### Example:
```elixir
queue = from_list([1, 2, 3, 4, 5])
5 == get_r(queue)
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before getting it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:207: :queue.get_r({[], []})
iex:58: (file)
```
"""
@spec get_r(queue_type()) :: any()
def get_r(queue) do
:queue.get_r(queue)
end
@doc """
Returns tuple `{:value, item}`, where Item is the front item of a queue,
or empty if a queue is `:empty`.
### Example:
```elixir
peek(new())
# :empty
queue = from_list([1, 2 ,3 ,4 ,5])
# {[5, 4, 3], [1, 2]}
peek(queue)
# {:value, 1}
```
"""
@spec peek(queue_type()) :: :empty | {:value, any()}
def peek(queue) do
:queue.peek(queue)
end
@doc """
Returns tuple `{:value, item}`, where Item is the rear item of a queue,
or empty if a queue is `:empty`.
### Example:
```elixir
peek_r(new())
# :empty
queue = from_list([1, 2 ,3 ,4 ,5])
# {[5, 4, 3], [1, 2]}
peek_r(queue)
# {:value, 5}
```
"""
@spec peek_r(queue_type()) :: :empty | {:value, any()}
def peek_r(queue) do
:queue.peek_r(queue)
end
@doc """
Please see `join/2` and `to_list/1`.
"""
@spec join_to_list(queue_type(), queue_type()) :: list(any())
def join_to_list(queue, queue1) do
:queue.join(queue, queue1)
|> :queue.to_list()
end
@doc """
Please see `join/2` and `to_list/1`.
"""
@spec join_to_list(list(any()), list(any())) :: queue_type()
def list_to_join(queue, queue1) do
:queue.join(:queue.from_list(queue), :queue.from_list(queue1))
end
# The "Okasaki API" is inspired by "Purely Functional Data Structures" by Chris Okasaki.
# It regards queues as lists. This API is by many regarded as strange and avoidable.
# For example, many reverse operations have lexically reversed names, some with more
# readable but perhaps less understandable aliases.
#
# Do not use these functions
@doc """
Inserts Item at the head of queue `q1`. Returns the new queue `q2`.
### Example:
```elixir
queue = cons(from_list([1, 2, 3]), 0)
to_list queue
# [0, 1, 2, 3]
```
"""
@spec cons(queue_type(), any()) :: queue_type()
def cons(queue, item) do
:queue.cons(item, queue)
end
@doc """
Returns the tail item of a queue.
### Example:
```elixir
queue = daeh(from_list([1, 2, 3]))
# 3
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before tailing it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:207: :queue.get_r({[], []})
iex:67: (file)
```
"""
@spec daeh(queue_type()) :: any()
def daeh(queue) do
:queue.daeh(queue)
end
@doc """
Returns the head item of a queue.
### Example:
```elixir
queue = head(from_list([1, 2, 3]))
# 1
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before heading it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:662: :queue.head({[], []})
iex:67: (file)
```
"""
@spec head(queue_type()) :: any()
def head(queue) do
:queue.head(queue)
end
@doc """
Please see `head/1`.
"""
@spec first(queue_type()) :: any()
def first(queue) do
:queue.head(queue)
end
@doc """
Returns a queue `q2` that is the result of removing the tail item from `q1`.
### Example:
```elixir
init(from_list([1, 2, 3]))
# {[2],[1]}
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before initing it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:270: :queue.drop_r({[], []})
iex:67: (file)
```
"""
@spec init(queue_type()) :: queue_type()
def init(queue) do
:queue.init(queue)
end
@doc """
Returns the tail item of a queue.
### Example:
```elixir
last(from_list([1,2 , 3]))
# 3
```
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before getting it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:207: :queue.get_r({[], []})
iex:67: (file)
```
"""
@spec last(queue_type()) :: any()
def last(queue) do
:queue.last(queue)
end
@spec liat(queue_type()) :: queue_type()
@doc """
It is like `init/1`.
"""
@spec liat(queue_type()) :: queue_type()
def liat(queue) do
:queue.liat(queue)
end
@doc """
Inserts Item as the tail item of a queue `q1`. Returns a new queue like `q2`.
### Example:
```elixir
queue = snoc(from_list([1, 2, 3]), 4)
# {[4, 3, 2], [1]}
to_list(queue)
# [1,2,3,4]
```
"""
@spec snoc(queue_type(), any()) :: queue_type()
def snoc(queue, item) do
:queue.snoc(queue, item)
end
@doc """
Returns a queue `q2` that is the result of removing the head item from `q1`.
> **Fails with reason empty if `queue` is empty.** You must check if it is empty before tailing it.
### Error output:
```
** (ErlangError) Erlang error: :empty
(stdlib 5.2.1) queue.erl:252: :queue.drop({[], []})
iex:67: (file)
```
"""
@spec tail(queue_type()) :: queue_type()
def tail(queue) do
:queue.tail(queue)
end
end