-
Notifications
You must be signed in to change notification settings - Fork 5
/
queue.ex
110 lines (80 loc) · 2.75 KB
/
queue.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
defmodule ExAlgo.Queue do
@moduledoc """
A basic queue implementation.
"""
@type underflow_error :: {:error, :underflow}
@type value_type :: any()
@type t :: %__MODULE__{left: [value_type()], right: [value_type()]}
@doc """
A queue consists of a left and a right list.
"""
defstruct left: [], right: []
@doc """
Creates a new empty Queue.
## Example
iex> Queue.new()
%Queue{left: [], right: []}
"""
@spec new() :: t()
def new, do: %__MODULE__{left: [], right: []}
@doc """
Creates a queue from a list.
## Example
iex> ExAlgo.Queue.from 1..3
%Queue{left: [3, 2, 1], right: []}
"""
@spec from(Enumerable.t()) :: t()
def from(enumerable),
do: %__MODULE__{left: enumerable |> Enum.to_list() |> Enum.reverse(), right: []}
@doc """
Enqueues item in left of the queue.
## Example
iex> Queue.new |> Queue.enqueue(10) |> Queue.enqueue(20)
%Queue{left: [20, 10], right: []}
"""
@spec enqueue(t(), value_type()) :: t()
def enqueue(%__MODULE__{left: left} = queue, item), do: %{queue | left: [item | left]}
@doc """
Dequeues the last item from the list.
## Example
iex> 1..4 |> Queue.from() |> Queue.dequeue()
{1, %Queue{left: [], right: [2, 3, 4]}}
iex> Queue.new |> Queue.dequeue()
{:error, :underflow}
"""
@spec dequeue(t()) :: {value_type(), t()} | underflow_error()
def dequeue(%__MODULE__{left: [], right: []}), do: {:error, :underflow}
def dequeue(%__MODULE__{right: [value | rest]} = queue), do: {value, %{queue | right: rest}}
def dequeue(%__MODULE__{left: left}),
do: %__MODULE__{left: [], right: Enum.reverse(left)} |> dequeue()
@doc """
Appends at the right of the list.
## Example
iex> Queue.new |> Queue.append(10)
%Queue{left: [], right: [10]}
iex> {_, queue} = 1..4 |> Queue.from() |> Queue.dequeue()
iex> queue |> Queue.enqueue(5) |> Queue.append(6)
%Queue{left: [5], right: [6, 2, 3, 4]}
"""
@spec append(t(), value_type()) :: t()
def append(%__MODULE__{right: right} = queue, item), do: %{queue | right: [item | right]}
@doc """
Returns a list representation of the queue.
## Example
iex> Queue.new() |> Queue.to_list()
[]
iex> Queue.from(1..4) |> Queue.to_list()
[4, 3, 2, 1]
iex> Queue.from(1..4)
...> |> Queue.dequeue()
...> |> then(fn {_, queue} -> queue end)
...> |> Queue.enqueue(-1)
...> |> Queue.append(10)
...> |> Queue.to_list()
[-1, 4, 3, 2, 10]
"""
@spec to_list(t()) :: List.t()
def to_list(%__MODULE__{left: left, right: []}), do: left
def to_list(%__MODULE__{left: [], right: right}), do: Enum.reverse(right)
def to_list(%__MODULE__{left: left, right: right}), do: left ++ Enum.reverse(right)
end