-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathqex.ex
More file actions
251 lines (207 loc) · 5.79 KB
/
qex.ex
File metadata and controls
251 lines (207 loc) · 5.79 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
defmodule Qex do
@moduledoc ~S"""
A `:queue` wrapper with improvements in API and addition of Protocol implementations
## Protocols
`Inspect`, `Collectable` and `Enumerable` are implemented
iex> inspect Qex.new
"#Qex<[]>"
iex> Enum.count Qex.new(1..5)
5
iex> Enum.empty? Qex.new
true
iex> Enum.map Qex.new([1, 2, 3]), &(&1 + 1)
[2, 3, 4]
iex> inspect Enum.into(1..5, %Qex{})
"#Qex<[1, 2, 3, 4, 5]>"
"""
@opaque t(type) :: %__MODULE__{:data => :queue.queue(type)}
@opaque t() :: %__MODULE__{:data => :queue.queue()}
defstruct data: :queue.new
@doc """
Create a new queue from a range
iex> inspect Qex.new(1..3)
"#Qex<[1, 2, 3]>"
Create a new queue from a list
iex> inspect Qex.new([1, 2, 3])
"#Qex<[1, 2, 3]>"
"""
@spec new([term] | Range.t) :: t
def new(init_data \\ [])
def new(x..y) do
%__MODULE__{data: :queue.from_list(Enum.to_list(x..y))}
end
def new(list) do
%__MODULE__{data: :queue.from_list(list)}
end
@doc """
Add an element to the back of the queue
iex> q = Qex.new([:mid])
iex> Enum.to_list Qex.push(q, :back)
[:mid, :back]
"""
@spec push(t, term) :: t
def push(%__MODULE__{data: q}, item) do
%__MODULE__{data: :queue.in(item, q)}
end
@doc """
Add an element to the front of the queue
iex> q = Qex.new([:mid])
iex> Enum.to_list Qex.push_front(q, :front)
[:front, :mid]
"""
@spec push_front(t, term) :: t
def push_front(%__MODULE__{data: q}, item) do
%__MODULE__{data: :queue.in_r(item, q)}
end
@doc """
Get and remove an element from the front of the queue
iex> q = Qex.new([:front, :mid])
iex> {{:value, item}, _q} = Qex.pop(q)
iex> item
:front
iex> q = Qex.new
iex> {empty, _q} = Qex.pop(q)
iex> empty
:empty
"""
@spec pop(t) :: {{:value, term}, t} | {:empty, t}
def pop(%__MODULE__{data: q}) do
case :queue.out(q) do
{{:value, v}, q} -> {{:value, v}, %__MODULE__{data: q}}
{:empty, q} -> {:empty, %__MODULE__{data: q}}
end
end
@spec pop!(t) :: {term, t} | no_return
def pop!(%__MODULE__{data: q}) do
case :queue.out(q) do
{{:value, v}, q} -> {v, %__MODULE__{data: q}}
{:empty, _q} -> raise "Queue is empty"
end
end
@doc """
Get and remove an element from the back of the queue
iex> q = Qex.new([:mid, :back])
iex> {{:value, item}, _q} = Qex.pop_back(q)
iex> item
:back
iex> q = Qex.new
iex> {empty, _q} = Qex.pop_back(q)
iex> empty
:empty
"""
@spec pop_back(t) :: {{:value, term}, t} | {:empty, t}
def pop_back(%__MODULE__{data: q}) do
case :queue.out_r(q) do
{{:value, v}, q} -> {{:value, v}, %__MODULE__{data: q}}
{:empty, q} -> {:empty, %__MODULE__{data: q}}
end
end
@spec pop_back!(t) :: {term, t} | no_return
def pop_back!(%__MODULE__{data: q}) do
case :queue.out_r(q) do
{{:value, v}, q} -> {v, %__MODULE__{data: q}}
{:empty, _q} -> raise "Queue is empty"
end
end
@doc """
Reverse a queue
iex> q = Qex.new(1..3)
iex> Enum.to_list q
[1, 2, 3]
iex> Enum.to_list Qex.reverse(q)
[3, 2, 1]
"""
@spec reverse(t) :: t
def reverse(%__MODULE__{data: q}) do
%__MODULE__{data: :queue.reverse(q)}
end
@doc """
Split a queue into two, the front n items (up to all items) are put in the first queue
Unlike :queue.split/2, which raises an ArgumentError when you ask to split more elements,
this matches the behavior of Enum.split/2 by silently returning the minimum of the
requested number of items and the complete queue.
iex> q = Qex.new 1..5
iex> {q1, q2} = Qex.split(q, 3)
iex> Enum.to_list q1
[1, 2, 3]
iex> Enum.to_list q2
[4, 5]
iex> {q_full, q_empty} = Qex.split(q, 1_000)
iex> Enum.to_list q_full
[1, 2, 3, 4, 5]
iex> Enum.to_list q_empty
[]
"""
@spec split(t, pos_integer) :: {t, t}
def split(%__MODULE__{data: q}, n) do
count = min(:queue.len(q), n)
with {q1, q2} <- :queue.split(count, q) do
{%__MODULE__{data: q1}, %__MODULE__{data: q2}}
end
end
@doc """
Join two queues together
iex> q1 = Qex.new 1..3
iex> q2 = Qex.new 4..5
iex> Enum.to_list Qex.join(q1, q2)
[1, 2, 3, 4, 5]
"""
@spec join(t, t) :: t
def join(%__MODULE__{data: q1}, %__MODULE__{data: q2}) do
%__MODULE__{data: :queue.join(q1, q2)}
end
@doc """
Return the first item in the queue in {:value, term} tuple,
return :empty if the queue is empty
iex> q1 = Qex.new 1..3
iex> Qex.first(q1)
{:value, 1}
iex> q2 = Qex.new []
iex> Qex.first(q2)
:empty
"""
@spec first(t) :: {:value, term} | :empty
def first(%__MODULE__{data: q}) do
:queue.peek(q)
end
@doc """
Retun the first item in the queue, raise if it's empty
iex> q1 = Qex.new 1..3
iex> Qex.first!(q1)
1
"""
@spec first!(t) :: term | no_return
def first!(%__MODULE__{data: q}) do
case :queue.peek(q) do
{:value, v} -> v
:empty -> raise "Queue is empty"
end
end
@doc """
Return the last item in the queue in {:value, term} tuple,
return :empty if the queue is empty
iex> q1 = Qex.new 1..3
iex> Qex.last(q1)
{:value, 3}
iex> q2 = Qex.new []
iex> Qex.last(q2)
:empty
"""
@spec last(t) :: {:value, term} | :empty
def last(%__MODULE__{data: q}) do
:queue.peek_r(q)
end
@doc """
Retun the last item in the queue, raise if it's empty
iex> q1 = Qex.new 1..3
iex> Qex.last!(q1)
3
"""
@spec last!(t) :: term | no_return
def last!(%__MODULE__{data: q}) do
case :queue.peek_r(q) do
{:value, v} -> v
:empty -> raise "Queue is empty"
end
end
end