Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add MutableLinkedList functionality #818

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/src/mutable_linked_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ delete!(l, idx) # delete element at index
delete!(l, range) # delete elements within range a:b
push!(l, data) # add element to end of list
pushfirst!(l, data) # add element to beginning of list
pop!(l) # remove element from end of list
popfirst!(l) # remove element from beginning of list
pop!(l) # remove element from end of list and return its data
popfirst!(l) # remove element from beginning of list and return its data
popat!(l, idx, [default]) # remove element at index and return its data
insert!(l, idx, data) # insert at index an element contatining data
splice!(l, idx, [elts]) # remove and return the element at index. Replacement elements will be spliced in if provided
splice!(l, range, [elts]) # remove and return the elements in the range. Replacement elements will be spliced in if provided
```

`MutableLinkedList` implements the Iterator interface, iterating over the list
Expand Down
105 changes: 105 additions & 0 deletions src/mutable_list.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,111 @@ function Base.popfirst!(l::MutableLinkedList)
return data
end

if isdefined(Base, :popat!) # We will overload if it is defined, else we define on our own
import Base: popat!
end

function popat!(l::MutableLinkedList, idx::Int)
@boundscheck 0 < idx <= l.len || throw(BoundsError(l, idx))
node = l.node
for i = 1:idx
node = node.next
end
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
l.len -= 1
return node.data
end

function popat!(l::MutableLinkedList, idx::Int, default)
if !(0 < idx <= l.len)
return default;
end
node = l.node
for i = 1:idx
node = node.next
end
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
l.len -= 1
return node.data
end

function Base.insert!(l::MutableLinkedList{T}, idx::Int, data) where T
@boundscheck 0 < idx <= l.len + 1 || throw(BoundsError(l, idx))
prev = l.node
for i in 1:idx-1
prev = prev.next
end
next = prev.next
node = ListNode{T}(data)
node.prev = prev
node.next = next
prev.next = node
next.prev = node
l.len += 1
return l
end

const _default_splice = []

function Base.splice!(l::MutableLinkedList{T}, idx::Int, ins=_default_splice) where T
@boundscheck 0 < idx <= l.len || throw(BoundsError(l, idx))
node = l.node
for i in 1:idx
node = node.next
end
data = node.data
prev = node.prev
next = node.next
if length(ins) == 0
prev.next = next
next.prev = prev
l.len -= 1
return data
end
insl = MutableLinkedList{T}(ins...)
insl.node.prev.next = next
insl.node.next.prev = prev
prev.next = insl.node.next
next.prev = insl.node.prev
l.len += insl.len - 1
return data
end

function Base.splice!(l::MutableLinkedList{T}, r::AbstractUnitRange{<:Integer}, ins=_default_splice) where T
@boundscheck (0 < first(r) <= l.len && last(r) <= l.len ) || throw(BoundsError(l, r))
len = length(r)
data = Vector{T}()
node = l.node
for i in 1:first(r)
node = node.next
end
prev = len > 0 ? node.prev : node
for i in 1:len
push!(data, node.data)
node = node.next
end
next = len > 0 ? node : node.next
if length(ins) == 0
prev.next = next
next.prev = prev
l.len -= len
return data
end
insl = MutableLinkedList{T}(ins...)
insl.node.prev.next = next
insl.node.next.prev = prev
prev.next = insl.node.next
next.prev = insl.node.prev
l.len += insl.len - len
return data
end

function Base.show(io::IO, node::ListNode)
x = node.data
print(io, "$(typeof(node))($x)")
Expand Down
90 changes: 90 additions & 0 deletions test/test_mutable_list.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,96 @@
end
end
end

@testset "insert / popat" begin
@testset "insert" begin
l = MutableLinkedList{Int}(1:n...)
@test_throws BoundsError insert!(l, 0, 0)
@test_throws BoundsError insert!(l, n+2, 0)
@test insert!(l, n+1, n+1) == MutableLinkedList{Int}(1:n+1...)
@test insert!(l, 1, 0) == MutableLinkedList{Int}(0:n+1...)
@test insert!(l, n+2, -1) == MutableLinkedList{Int}(0:n..., -1, n+1)
for i=n:-1:1
insert!(l, n+2, i)
end
@test l == MutableLinkedList{Int}(0:n..., 1:n..., -1, n+1)
@test l.len == 2n + 3
end

@testset "popat" begin
l = MutableLinkedList{Int}(1:n...)
@test_throws BoundsError popat!(l, 0)
@test_throws BoundsError popat!(l, n+1)
@test popat!(l, 0, missing) === missing
@test popat!(l, n+1, Inf) === Inf
for i=2:n-1
@test popat!(l, 2) == i
end
@test l == MutableLinkedList{Int}(1,n)
@test l.len == 2

l2 = MutableLinkedList{Int}(1:n...)
for i=n-1:-1:2
@test popat!(l2, l2.len-1, 0) == i
end
@test l2 == MutableLinkedList{Int}(1,n)
@test l2.len == 2
@test popat!(l2, 1) == 1
@test popat!(l2, 1) == n
@test l2 == MutableLinkedList{Int}()
@test l2.len == 0
@test_throws BoundsError popat!(l2, 1)
end
end

@testset "splice" begin
@testset "no replacement" begin
l = MutableLinkedList{Int}(1:2n...)
@test splice!(l, n:1) == Int[]
@test l == MutableLinkedList{Int}(1:2n...)
@test collect(n+1:2n) == splice!(l, n+1:2n)
@test l == MutableLinkedList{Int}(1:n...)
for i = n:-1:1
@test i == splice!(l, i)
end
@test l == MutableLinkedList{Int}()
@test_throws BoundsError splice!(l, 1)

end
@testset "with replacement" begin
l = MutableLinkedList{Int}(1)
for i = 2:n
@test splice!(l, i-1:i-2, i) == Int[]
@test last(l) == i
@test l.len == i
end
@test l == MutableLinkedList{Int}(1:n...,)
for i = 1:n
@test splice!(l, 1:0, i) == Int[]
@test first(l) == 1
@test l[2] == i
@test l.len == i + n
end
@test l == MutableLinkedList{Int}(1, n:-1:1..., 2:n...)
previousdata = l[1:l.len]
for i = 1:2n
@test splice!(l, i, i+2n) == previousdata[i]
@test l[i] == i+2n
end
@test l == MutableLinkedList{Int}(2n+1:4n...)
@test splice!(l, n+1:2n, [3n+1, 3n+2]) == [3n+1:4n...,]
@test l == MutableLinkedList{Int}(2n+1:3n+2...)
@test l.len == n+2
for i=1:n+2
@test splice!(l, i, -i) == i+2n
end
@test l == MutableLinkedList{Int}(-1:-1:-n-2...)
@test l.len == n+2
@test splice!(l, 1:n+2, 0) == collect(-1:-1:-n-2)
@test l == MutableLinkedList{Int}(0)
@test l.len == 1
end
end
end

@testset "random operations" begin
Expand Down