diff --git a/docs/src/mutable_linked_list.md b/docs/src/mutable_linked_list.md index fd900e767..f0b1ebabf 100644 --- a/docs/src/mutable_linked_list.md +++ b/docs/src/mutable_linked_list.md @@ -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 diff --git a/src/mutable_list.jl b/src/mutable_list.jl index 3a650d071..90355ebdd 100644 --- a/src/mutable_list.jl +++ b/src/mutable_list.jl @@ -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)") diff --git a/test/test_mutable_list.jl b/test/test_mutable_list.jl index 36fb50fe5..0b7f89a1c 100644 --- a/test/test_mutable_list.jl +++ b/test/test_mutable_list.jl @@ -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