Skip to content

Commit 98ad0f2

Browse files
authored
Merge pull request #800 from lfenzo/docs-revamp-deque
Improved Deque Documentation + docstrings
2 parents 515001a + 09e4873 commit 98ad0f2

File tree

2 files changed

+143
-100
lines changed

2 files changed

+143
-100
lines changed

docs/src/deque.md

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,51 @@
11
# Deque
22

3-
The `Deque` type implements a double-ended queue using a list of blocks.
4-
This data structure supports constant-time insertion/removal of elements
5-
at both ends of a sequence.
6-
7-
Usage:
8-
9-
```julia
10-
a = Deque{Int}()
11-
isempty(a) # test whether the dequeue is empty
12-
length(a) # get the number of elements
13-
push!(a, 10) # add an element to the back
14-
pop!(a) # remove an element from the back
15-
pushfirst!(a, 20) # add an element to the front
16-
popfirst!(a) # remove an element from the front
17-
first(a) # get the element at the front
18-
last(a) # get the element at the back
19-
```
3+
A Deque (short for Double-ended Queue) is an abstract data type that generalizes
4+
a Queue for which elements can be added to or removed from both the front (head)
5+
and the back (tail) in $O(1)$ time complexity.
6+
7+
The type `Deque` implements the Double-ended Queue as a list of fixed-size blocks
8+
using an [unrolled linked list](https://en.wikipedia.org/wiki/Unrolled_linked_list).
209

21-
*Note:* Julia's `Vector` type also provides this interface, and thus can
22-
be used as a deque. However, the `Deque` type in this package is
23-
implemented as a list of contiguous blocks (default size = 2K). As a
24-
deque grows, new blocks may be created and linked to existing blocks.
25-
This way avoids the copying when growing a vector.
10+
!!! note
11+
Julia's `Vector` type also provides this interface, and thus can
12+
be used as a deque. However, the `Deque` type in DataStructures.jl is
13+
implemented as a list of contiguous blocks (default size = 1 kilo-byte). As a
14+
Deque grows, new blocks are created and linked to existing blocks.
15+
This apprach prevents copying operations that take place when growing a `Vector`.
2616

2717
Benchmark shows that the performance of `Deque` is comparable to
28-
`Vector` on `push!`, and is noticeably faster on `pushfirst!` (by about
18+
`Vector` on `push!`, but is noticeably faster on `pushfirst!` (by about
2919
30% to 40%).
20+
21+
22+
## Constructors
23+
24+
```@autodocs
25+
Modules = [DataStructures]
26+
Pages = ["src/deque.jl"]
27+
Order = [:type] # only types
28+
```
29+
30+
## Usage
31+
32+
The `Deque` implements the following methods:
33+
34+
- [`==(x::Deque, y::Deque)`](@ref)
35+
- [`empty!(d::Deque{T}) where T`](@ref)
36+
- [`first(d::Deque)`](@ref)
37+
- [`isempty(d::Deque)`](@ref)
38+
- [`last(d::Deque)`](@ref)
39+
- [`length(d::Deque)`](@ref)
40+
- [`pop!(d::Deque{T}) where T`](@ref)
41+
- [`popfirst!(d::Deque{T}) where T`](@ref)
42+
- [`push!(d::Deque{T}, x) where T`](@ref)
43+
- [`pushfirst!(d::Deque{T}, x) where T`](@ref)
44+
45+
-------
46+
47+
```@autodocs
48+
Modules = [DataStructures]
49+
Pages = ["src/deque.jl"]
50+
Order = [:function] # only functions
51+
```

src/deque.jl

Lines changed: 98 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,16 @@ const DEFAULT_DEQUEUE_BLOCKSIZE = 1024
6060

6161
"""
6262
Deque{T}
63+
Deque{T}(blksize::Int) where T
6364
64-
The Deque type implements a double-ended queue using a list of blocks.
65-
This data structure supports constant-time insertion/removal
66-
of elements at both ends of a sequence.
65+
Constructs `Deque` object for elements of type `T`.
6766
67+
Parameters
68+
----------
69+
70+
`T::Type` Deque element data type.
71+
72+
`blksize::Int` Deque block size (in bytes). Default = 1024.
6873
"""
6974
mutable struct Deque{T}
7075
nblocks::Int
@@ -81,43 +86,54 @@ mutable struct Deque{T}
8186
Deque{T}() where {T} = Deque{T}(DEFAULT_DEQUEUE_BLOCKSIZE)
8287
end
8388

84-
Base.isempty(q::Deque) = q.len == 0
85-
Base.length(q::Deque) = q.len
86-
num_blocks(q::Deque) = q.nblocks
89+
"""
90+
isempty(d::Deque)
91+
92+
Verifies if deque `d` is empty.
93+
"""
94+
Base.isempty(d::Deque) = d.len == 0
95+
96+
"""
97+
length(d::Deque)
98+
99+
Returns the number of elements in deque `d`.
100+
"""
101+
Base.length(d::Deque) = d.len
102+
num_blocks(d::Deque) = d.nblocks
87103
Base.eltype(::Type{Deque{T}}) where T = T
88104

89105
"""
90-
first(q::Deque)
106+
first(d::Deque)
91107
92-
Returns the first element of the deque `q`.
108+
Returns the first element of the deque `d`.
93109
"""
94-
function Base.first(q::Deque)
95-
isempty(q) && throw(ArgumentError("Deque must be non-empty"))
96-
blk = q.head
110+
function Base.first(d::Deque)
111+
isempty(d) && throw(ArgumentError("Deque must be non-empty"))
112+
blk = d.head
97113
return blk.data[blk.front]
98114
end
99115

100116
"""
101-
last(q::Deque)
117+
last(d::Deque)
102118
103-
Returns the last element of the deque `q`.
119+
Returns the last element of the deque `d`.
104120
"""
105-
function Base.last(q::Deque)
106-
isempty(q) && throw(ArgumentError("Deque must be non-empty"))
107-
blk = q.rear
121+
function Base.last(d::Deque)
122+
isempty(d) && throw(ArgumentError("Deque must be non-empty"))
123+
blk = d.rear
108124
return blk.data[blk.back]
109125
end
110126

111127

112128
# Iteration
113129

114130
struct DequeIterator{T}
115-
q::Deque{T}
131+
d::Deque{T}
116132
end
117133

118-
Base.last(qi::DequeIterator) = last(qi.q)
134+
Base.last(di::DequeIterator) = last(di.d)
119135

120-
function Base.iterate(qi::DequeIterator{T}, (cb, i) = (qi.q.head, qi.q.head.front)) where T
136+
function Base.iterate(di::DequeIterator{T}, (cb, i) = (di.d.head, di.d.head.front)) where T
121137
i > cb.back && return nothing
122138
x = cb.data[i]
123139

@@ -132,7 +148,7 @@ end
132148

133149
# Backwards deque iteration
134150

135-
function Base.iterate(qi::Iterators.Reverse{<:Deque}, (cb, i) = (qi.itr.rear, qi.itr.rear.back))
151+
function Base.iterate(di::Iterators.Reverse{<:Deque}, (cb, i) = (di.itr.rear, di.itr.rear.back))
136152
i < cb.front && return nothing
137153
x = cb.data[i]
138154

@@ -146,21 +162,21 @@ function Base.iterate(qi::Iterators.Reverse{<:Deque}, (cb, i) = (qi.itr.rear, qi
146162
return (x, (cb, i))
147163
end
148164

149-
Base.iterate(q::Deque{T}, s...) where {T} = iterate(DequeIterator{T}(q), s...)
165+
Base.iterate(d::Deque{T}, s...) where {T} = iterate(DequeIterator{T}(d), s...)
150166

151-
Base.length(qi::DequeIterator{T}) where {T} = qi.q.len
167+
Base.length(di::DequeIterator{T}) where {T} = di.d.len
152168

153-
Base.collect(q::Deque{T}) where {T} = T[x for x in q]
169+
Base.collect(d::Deque{T}) where {T} = T[x for x in d]
154170

155171
# Showing
156172

157-
function Base.show(io::IO, q::Deque)
158-
print(io, "Deque [$(collect(q))]")
173+
function Base.show(io::IO, d::Deque)
174+
print(io, "Deque [$(collect(d))]")
159175
end
160176

161-
function Base.dump(io::IO, q::Deque)
162-
println(io, "Deque (length = $(q.len), nblocks = $(q.nblocks))")
163-
cb::DequeBlock = q.head
177+
function Base.dump(io::IO, d::Deque)
178+
println(io, "Deque (length = $(d.len), nblocks = $(d.nblocks))")
179+
cb::DequeBlock = d.head
164180
i = 1
165181
while true
166182
print(io, "block $i [$(cb.front):$(cb.back)] ==> ")
@@ -184,38 +200,38 @@ end
184200
# Manipulation
185201

186202
"""
187-
empty!(q::Deque{T})
203+
empty!(d::Deque{T}) where T
188204
189-
Reset the deque.
205+
Reset the deque `d`.
190206
"""
191-
function Base.empty!(q::Deque{T}) where T
207+
function Base.empty!(d::Deque{T}) where T
192208
# release all blocks except the head
193-
if q.nblocks > 1
194-
cb::DequeBlock{T} = q.rear
195-
while cb != q.head
209+
if d.nblocks > 1
210+
cb::DequeBlock{T} = d.rear
211+
while cb != d.head
196212
empty!(cb.data)
197213
cb = cb.prev
198214
end
199215
end
200216

201217
# clean the head block (but retain the block itself)
202-
reset!(q.head, 1)
218+
reset!(d.head, 1)
203219

204220
# reset queue fields
205-
q.nblocks = 1
206-
q.len = 0
207-
q.rear = q.head
208-
return q
221+
d.nblocks = 1
222+
d.len = 0
223+
d.rear = d.head
224+
return d
209225
end
210226

211227

212228
"""
213-
push!(q::Deque{T}, x)
229+
push!(d::Deque{T}, x) where T
214230
215-
Add an element to the back
231+
Add an element to the back of deque `d`.
216232
"""
217-
function Base.push!(q::Deque{T}, x) where T
218-
rear = q.rear
233+
function Base.push!(d::Deque{T}, x) where T
234+
rear = d.rear
219235

220236
if isempty(rear)
221237
rear.front = 1
@@ -225,24 +241,24 @@ function Base.push!(q::Deque{T}, x) where T
225241
if rear.back < rear.capa
226242
@inbounds rear.data[rear.back += 1] = convert(T, x)
227243
else
228-
new_rear = rear_deque_block(T, q.blksize)
244+
new_rear = rear_deque_block(T, d.blksize)
229245
new_rear.back = 1
230246
new_rear.data[1] = convert(T, x)
231247
new_rear.prev = rear
232-
q.rear = rear.next = new_rear
233-
q.nblocks += 1
248+
d.rear = rear.next = new_rear
249+
d.nblocks += 1
234250
end
235-
q.len += 1
236-
return q
251+
d.len += 1
252+
return d
237253
end
238254

239255
"""
240-
pushfirst!(q::Deque{T}, x)
256+
pushfirst!(d::Deque{T}, x) where T
241257
242-
Add an element to the front
258+
Add an element to the front of deque `d`.
243259
"""
244-
function Base.pushfirst!(q::Deque{T}, x) where T
245-
head = q.head
260+
function Base.pushfirst!(d::Deque{T}, x) where T
261+
head = d.head
246262

247263
if isempty(head)
248264
n = head.capa
@@ -253,65 +269,65 @@ function Base.pushfirst!(q::Deque{T}, x) where T
253269
if head.front > 1
254270
@inbounds head.data[head.front -= 1] = convert(T, x)
255271
else
256-
n::Int = q.blksize
272+
n::Int = d.blksize
257273
new_head = head_deque_block(T, n)
258274
new_head.front = n
259275
new_head.data[n] = convert(T, x)
260276
new_head.next = head
261-
q.head = head.prev = new_head
262-
q.nblocks += 1
277+
d.head = head.prev = new_head
278+
d.nblocks += 1
263279
end
264-
q.len += 1
265-
return q
280+
d.len += 1
281+
return d
266282
end
267283

268284
"""
269-
pop!(q::Deque{T})
285+
pop!(d::Deque{T}) where T
270286
271-
Remove the element at the back
287+
Remove the element at the back of deque `d`.
272288
"""
273-
function Base.pop!(q::Deque{T}) where T
274-
isempty(q) && throw(ArgumentError("Deque must be non-empty"))
275-
rear = q.rear
289+
function Base.pop!(d::Deque{T}) where T
290+
isempty(d) && throw(ArgumentError("Deque must be non-empty"))
291+
rear = d.rear
276292
@assert rear.back >= rear.front
277293

278294
@inbounds x = rear.data[rear.back]
279295
rear.back -= 1
280296
if rear.back < rear.front
281-
if q.nblocks > 1
297+
if d.nblocks > 1
282298
# release and detach the rear block
283299
empty!(rear.data)
284-
q.rear = rear.prev::DequeBlock{T}
285-
q.rear.next = q.rear
286-
q.nblocks -= 1
300+
d.rear = rear.prev::DequeBlock{T}
301+
d.rear.next = d.rear
302+
d.nblocks -= 1
287303
end
288304
end
289-
q.len -= 1
305+
d.len -= 1
290306
return x
291307
end
292308

293309
"""
294-
popfirst!(q::Deque{T})
310+
popfirst!(d::Deque{T}) where T
295311
296-
Remove the element at the front
312+
Remove the element at the front of deque `d`.
297313
"""
298-
function Base.popfirst!(q::Deque{T}) where T
299-
isempty(q) && throw(ArgumentError("Deque must be non-empty"))
300-
head = q.head
314+
function Base.popfirst!(d::Deque{T}) where T
315+
isempty(d) && throw(ArgumentError("Deque must be non-empty"))
316+
head = d.head
301317
@assert head.back >= head.front
302318

303319
@inbounds x = head.data[head.front]
304320
head.front += 1
305321
if head.back < head.front
306-
if q.nblocks > 1
322+
if d.nblocks > 1
307323
# release and detach the head block
308324
empty!(head.data)
309-
q.head = head.next::DequeBlock{T}
310-
q.head.prev = q.head
311-
q.nblocks -= 1
325+
d.head = head.next::DequeBlock{T}
326+
d.head.prev = d.head
327+
d.nblocks -= 1
312328
end
313329
end
314-
q.len -= 1
330+
d.len -= 1
315331
return x
316332
end
317333

@@ -324,6 +340,11 @@ function Base.hash(x::Deque, h::UInt)
324340
return h
325341
end
326342

343+
"""
344+
==(x::Deque, y::Deque)
345+
346+
Verify if the deques `x` and `y` are equal in terms of their contents.
347+
"""
327348
function Base.:(==)(x::Deque, y::Deque)
328349
length(x) != length(y) && return false
329350
for (i, j) in zip(x, y)

0 commit comments

Comments
 (0)