diff --git a/README.md b/README.md index 5dcffc40..be9f30b7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ This package implements a variety of data structures, including - SwissDict (inspired from [SwissTables](https://abseil.io/blog/20180927-swisstables)) - Dictionaries with Defaults - Trie -- Linked List and Mutable Linked List - Sorted Dict, Sorted Multi-Dict and Sorted Set - DataStructures.IntSet - SparseIntSet diff --git a/docs/src/index.md b/docs/src/index.md index 416b07d1..c045416c 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -18,7 +18,6 @@ This package implements a variety of data structures, including - SwissDict (inspired from [SwissTables](https://abseil.io/blog/20180927-swisstables)) - Dictionaries with Defaults - Trie -- Linked List and Mutable Linked List - Sorted Dict, Sorted Multi-Dict and Sorted Set - DataStructures.IntSet - SparseIntSet @@ -45,8 +44,6 @@ Pages = [ "default_dict.md", "robin_dict.md", "trie.md", - "linked_list.md", - "mutable_linked_list.md", "intset.md", "sorted_containers.md", "sparse_int_set.md", diff --git a/docs/src/linked_list.md b/docs/src/linked_list.md deleted file mode 100644 index f3d3de4b..00000000 --- a/docs/src/linked_list.md +++ /dev/null @@ -1,24 +0,0 @@ -# Linked List - -A list of sequentially linked nodes. This allows efficient insertion of -nodes to the front of the list: - -```julia -julia> l1 = nil() -nil() - -julia> l2 = cons(1, l1) -list(1) - -julia> l3 = list(2, 3) -list(2, 3) - -julia> l4 = cat(l1, l2, l3) -list(1, 2, 3) - -julia> l5 = map((x) -> x*2, l4) -list(2, 4, 6) - -julia> for i in l5; print(i); end -246 -``` diff --git a/docs/src/mutable_linked_list.md b/docs/src/mutable_linked_list.md deleted file mode 100644 index fd900e76..00000000 --- a/docs/src/mutable_linked_list.md +++ /dev/null @@ -1,37 +0,0 @@ -# Mutable Linked List - -The `MutableLinkedList` type implements a doubly linked list with mutable nodes. -This data structure supports constant-time insertion/removal of elements -at both ends of the list. - -Usage: - -```julia -l = MutableLinkedList{T}() # initialize an empty list of type T -l = MutableLinkedList{T}(elts...) # initialize a list with elements of type T -isempty(l) # test whether list is empty -length(l) # get the number of elements in list -collect(l) # return a vector consisting of list elements -eltype(l) # return type of list -first(l) # return value of first element of list -last(l) # return value of last element of list -l1 == l2 # test lists for equality -map(f, l) # return list with f applied to elements -filter(f, l) # return list of elements where f(el) == true -reverse(l) # return reversed list -copy(l) # return a copy of list -getindex(l, idx) || l[idx] # get value at index -getindex(l, range) || l[range] # get values within range a:b -setindex!(l, data, idx) # set value at index to data -append!(l1, l2) # attach l2 at the end of l1 -append!(l, elts...) # attach elements at end of list -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 -``` - -`MutableLinkedList` implements the Iterator interface, iterating over the list -from first to last. diff --git a/src/DataStructures.jl b/src/DataStructures.jl index 5e5d767e..e925cc2a 100644 --- a/src/DataStructures.jl +++ b/src/DataStructures.jl @@ -30,8 +30,6 @@ module DataStructures export Trie, subtrie, keys_with_prefix, partial_path, find_prefixes - export LinkedList, Nil, Cons, nil, cons, head, tail, list - export MutableLinkedList export SortedDict, SortedMultiDict, SortedSet export SMDSemiToken, SMDToken export SetSemiToken @@ -78,8 +76,6 @@ module DataStructures include("fenwick.jl") - include("list.jl") - include("mutable_list.jl") include("balanced_tree.jl") include("tokens.jl") diff --git a/src/list.jl b/src/list.jl deleted file mode 100644 index fbc93b96..00000000 --- a/src/list.jl +++ /dev/null @@ -1,132 +0,0 @@ -abstract type LinkedList{T} end - -Base.eltype(::Type{<:LinkedList{T}}) where T = T - -mutable struct Nil{T} <: LinkedList{T} -end - -mutable struct Cons{T} <: LinkedList{T} - head::T - tail::LinkedList{T} -end - -cons(h, t::LinkedList{T}) where {T} = Cons{T}(h, t) - -nil(T) = Nil{T}() -nil() = nil(Any) - -head(x::Cons) = x.head -tail(x::Cons) = x.tail - -Base.:(==)(x::Nil, y::Nil) = true -Base.:(==)(x::Cons, y::Cons) = (x.head == y.head) && (x.tail == y.tail) - -function Base.show(io::IO, l::LinkedList{T}) where T - if isa(l,Nil) - if T === Any - print(io, "nil()") - else - print(io, "nil(", T, ")") - end - else - print(io, "list(") - show(io, head(l)) - for t in tail(l) - print(io, ", ") - show(io, t) - end - print(io, ")") - end -end - -list() = nil() - -function list(elts...) - l = nil(Base.promote_typeof(elts...)) - for i=length(elts):-1:1 - l = cons(elts[i],l) - end - return l -end - -function list(elts::T...) where T - l = nil(T) - for i=length(elts):-1:1 - l = cons(elts[i],l) - end - return l -end - -Base.length(l::Nil) = 0 - -function Base.length(l::Cons) - n = 0 - for i in l - n += 1 - end - return n -end - -Base.map(f::Base.Callable, l::Nil) = l - -function Base.map(f::Base.Callable, l::Cons{T}) where T - first = f(l.head) - l2 = cons(first, nil(typeof(first) <: T ? T : typeof(first))) - for h in l.tail - l2 = cons(f(h), l2) - end - reverse(l2) -end - -function Base.filter(f::Function, l::LinkedList{T}) where T - l2 = nil(T) - for h in l - if f(h) - l2 = cons(h, l2) - end - end - reverse(l2) -end - -function Base.reverse(l::LinkedList{T}) where T - l2 = nil(T) - for h in l - l2 = cons(h, l2) - end - return l2 -end - -Base.copy(l::Nil) = l - -function Base.copy(l::Cons) - l2 = reverse(reverse(l)) -end - -Base.cat(lst::LinkedList) = lst - -function Base.cat(lst::LinkedList, lsts::LinkedList...) - T = typeof(lst).parameters[1] - n = length(lsts) - for i = 1:n - T2 = typeof(lsts[i]).parameters[1] - T = typejoin(T, T2) - end - - l2 = nil(T) - for h in lst - l2 = cons(h, l2) - end - - for i = 1:n - for h in lsts[i] - l2 = cons(h, l2) - end - end - - reverse(l2) -end - -Base.iterate(l::LinkedList, ::Nil) = nothing -function Base.iterate(l::LinkedList, state::Cons = l) - state.head, state.tail -end diff --git a/src/mutable_list.jl b/src/mutable_list.jl deleted file mode 100644 index cf085d59..00000000 --- a/src/mutable_list.jl +++ /dev/null @@ -1,266 +0,0 @@ -mutable struct ListNode{T} - data::T - prev::ListNode{T} - next::ListNode{T} - function ListNode{T}() where T - node = new{T}() - node.next = node - node.prev = node - return node - end - function ListNode{T}(data) where T - node = new{T}(data) - return node - end -end - -mutable struct MutableLinkedList{T} - len::Int - node::ListNode{T} - function MutableLinkedList{T}() where T - l = new{T}() - l.len = 0 - l.node = ListNode{T}() - l.node.next = l.node - l.node.prev = l.node - return l - end -end - -MutableLinkedList() = MutableLinkedList{Any}() - -function MutableLinkedList{T}(elts...) where T - l = MutableLinkedList{T}() - for elt in elts - push!(l, elt) - end - return l -end - -Base.iterate(l::MutableLinkedList) = l.len == 0 ? nothing : (l.node.next.data, l.node.next.next) -Base.iterate(l::MutableLinkedList, n::ListNode) = n === l.node ? nothing : (n.data, n.next) - -Base.isempty(l::MutableLinkedList) = l.len == 0 -Base.length(l::MutableLinkedList) = l.len -Base.collect(l::MutableLinkedList{T}) where T = T[x for x in l] -Base.eltype(::Type{<:MutableLinkedList{T}}) where T = T -Base.lastindex(l::MutableLinkedList) = l.len - -function Base.first(l::MutableLinkedList) - isempty(l) && throw(ArgumentError("List is empty")) - return l.node.next.data -end - -function Base.last(l::MutableLinkedList) - isempty(l) && throw(ArgumentError("List is empty")) - return l.node.prev.data -end - -Base.:(==)(l1::MutableLinkedList{T}, l2::MutableLinkedList{S}) where {T,S} = false - -function Base.:(==)(l1::MutableLinkedList{T}, l2::MutableLinkedList{T}) where T - length(l1) == length(l2) || return false - for (i, j) in zip(l1, l2) - i == j || return false - end - return true -end - -function Base.map(f::Base.Callable, l::MutableLinkedList{T}) where T - if isempty(l) && f isa Function - S = Core.Compiler.return_type(f, Tuple{T}) - return MutableLinkedList{S}() - elseif isempty(l) && f isa Type - return MutableLinkedList{f}() - else - S = typeof(f(first(l))) - l2 = MutableLinkedList{S}() - for h in l - el = f(h) - if el isa S - push!(l2, el) - else - R = typejoin(S, typeof(el)) - l2 = MutableLinkedList{R}(collect(l2)...) - push!(l2, el) - end - end - return l2 - end -end - -function Base.filter(f::Function, l::MutableLinkedList{T}) where T - l2 = MutableLinkedList{T}() - for h in l - if f(h) - push!(l2, h) - end - end - return l2 -end - -function Base.reverse(l::MutableLinkedList{T}) where T - l2 = MutableLinkedList{T}() - for h in l - pushfirst!(l2, h) - end - return l2 -end - -function Base.copy(l::MutableLinkedList{T}) where T - l2 = MutableLinkedList{T}() - for h in l - push!(l2, h) - end - return l2 -end - -function Base.getindex(l::MutableLinkedList, idx::Int) - @boundscheck 0 < idx <= l.len || throw(BoundsError(l, idx)) - node = l.node - for i in 1:idx - node = node.next - end - return node.data -end - -function Base.getindex(l::MutableLinkedList{T}, r::UnitRange) where T - @boundscheck 0 < first(r) < last(r) <= l.len || throw(BoundsError(l, r)) - l2 = MutableLinkedList{T}() - node = l.node - for i in 1:first(r) - node = node.next - end - len = length(r) - for j in 1:len - push!(l2, node.data) - node = node.next - end - l2.len = len - return l2 -end - -function Base.setindex!(l::MutableLinkedList{T}, data, idx::Int) where T - @boundscheck 0 < idx <= l.len || throw(BoundsError(l, idx)) - node = l.node - for i in 1:idx - node = node.next - end - node.data = convert(T, data) - return l -end - -function Base.append!(l1::MutableLinkedList{T}, l2::MutableLinkedList{T}) where T - l1.node.prev.next = l2.node.next # l1's last's next is now l2's first - l2.node.prev.next = l1.node # l2's last's next is now l1.node - l2.node.next.prev = l1.node.prev # l2's first's prev is now l1's last - l1.node.prev = l2.node.prev # l1's first's prev is now l2's last - l1.len += length(l2) - # make l2 empty - l2.node.prev = l2.node - l2.node.next = l2.node - l2.len = 0 - return l1 -end - -function Base.append!(l::MutableLinkedList, elts...) - for elt in elts - for v in elt - push!(l, v) - end - end - return l -end - -function Base.delete!(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 l -end - -function Base.delete!(l::MutableLinkedList, r::UnitRange) - @boundscheck 0 < first(r) < last(r) <= l.len || throw(BoundsError(l, r)) - node = l.node - for i in 1:first(r) - node = node.next - end - prev = node.prev - len = length(r) - for j in 1:len - node = node.next - end - next = node - prev.next = next - next.prev = prev - l.len -= len - return l -end - -function Base.push!(l::MutableLinkedList{T}, data) where T - oldlast = l.node.prev - node = ListNode{T}(data) - node.next = l.node - node.prev = oldlast - l.node.prev = node - oldlast.next = node - l.len += 1 - return l -end - -function Base.push!(l::MutableLinkedList{T}, data1, data...) where T - push!(l, data1) - for v in data - push!(l, v) - end - return l -end - -function Base.pushfirst!(l::MutableLinkedList{T}, data) where T - oldfirst = l.node.next - node = ListNode{T}(data) - node.prev = l.node - node.next = oldfirst - l.node.next = node - oldfirst.prev = node - l.len += 1 - return l -end - -function Base.pop!(l::MutableLinkedList) - isempty(l) && throw(ArgumentError("List must be non-empty")) - last = l.node.prev.prev - data = l.node.prev.data - last.next = l.node - l.node.prev = last - l.len -= 1 - return data -end - -function Base.popfirst!(l::MutableLinkedList) - isempty(l) && throw(ArgumentError("List must be non-empty")) - first = l.node.next.next - data = l.node.next.data - first.prev = l.node - l.node.next = first - l.len -= 1 - return data -end - -function Base.show(io::IO, node::ListNode) - x = node.data - print(io, "$(typeof(node))($x)") -end - -function Base.show(io::IO, l::MutableLinkedList) - print(io, typeof(l), '(') - join(io, l, ", ") - print(io, ')') -end diff --git a/test/runtests.jl b/test/runtests.jl index d44fe5bc..ff0be76c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,8 +24,6 @@ tests = [ "minmax_heap", "default_dict", "trie", - "list", - "mutable_list", "multi_dict", "circular_buffer", "sorting", diff --git a/test/test_list.jl b/test/test_list.jl deleted file mode 100644 index 389d481e..00000000 --- a/test/test_list.jl +++ /dev/null @@ -1,124 +0,0 @@ -@testset "LinkedList" begin - - @testset "basic tests" begin - l = list(2, 3) - @test length(l) == 2 - @test eltype(l) == Int - @test eltype(typeof(l)) == Int - end - - @testset "l0" begin - l0 = nil(Char) - @test length(l0) == 0 - @test l0 == nil(Char) - @test l0 == nil() - @test sprint(show,l0) == "nil(Char)" - end - - @testset "l1" begin - l1 = nil() - @test length(l1) == 0 - @test l1 == nil() - @test l1 == nil(Int) - @test sprint(show,l1) == "nil()" - @test typeof(list()) === typeof(l1) - @test copy(l1) == l1 - @test map((x) -> x*2,l1) == l1 - end - - @testset "l2" begin - l1 = nil() - l2 = cons(1, l1) - @test length(l2) == 1 - @test head(l2) == 1 - @test l2 == cons(1, l1) - @test l2 == list(1) - @test sprint(show,l2) == "list(1)" - @test cat(l2) == l2 - end - - @testset "l3" begin - l3 = list(2, 3) - @test isa(l3, Cons{Int}) - @test length(l3) == 2 - @test head(l3) == 2 - @test head(tail(l3)) == 3 - @test l3 == list(2,3) - @test collect(l3) == [2; 3] - @test collect(copy(l3)) == [2; 3] - @test sprint(show,l3) == "list(2, 3)" - end - - @testset "l4" begin - l1 = nil() - l2 = cons(1, l1) - l3 = list(2, 3) - l4 = cat(l1, l2, l3) - @test length(l4) == 3 - @test l4 == list(1, 2, 3) - @test collect(l4) == [1; 2; 3] - @test collect(copy(l4)) == [1; 2; 3] - @test sprint(show,l4) == "list(1, 2, 3)" - end - - @testset "l5" begin - l4 = list(1, 2, 3) - l5 = map((x) -> x*2, l4) - @test isa(l5, Cons{Int}) - @test collect(l5) == [2; 4; 6] - end - - @testset "l5b" begin - l5 = list(2, 4, 6) - l5b = map((x) -> "$x", l5) - @test isa(l5b, Cons{String}) - @test collect(l5b) == ["2"; "4"; "6"] - end - - @testset "l5" begin - l5 = list(2, 4, 6) - l6 = filter((x) -> x < 6, l5) - @test length(l6) == 2 - @test l6.head == 2 - @test l6.tail.head == 4 - @test collect(l6) == [2, 4] - end - - @testset "l7" begin - l6 = list(2, 4) - l7 = reverse(l6) - @test length(l7) == 2 - @test l7.head == 4 - @test l7.tail.head == 2 - end - - @testset "l8" begin - l5b = list("2", "4", "6") - l6 = list(2, 4) - l8 = cat(l5b, l6) - @test collect(l8) == ["2"; "4"; "6"; 2; 4] - end - - @testset "l9" begin - l9 = cat(list(1, 2), list(3.0, 4.0)) - @test isa(l9, Cons{Real}) - @test collect(l9) == [1; 2; 3.0; 4.0] - end - - @testset "l10" begin - l10 = list(2, 4, 5.6, 10.5) - @test collect(l10) == [2; 4; 5.6; 10.5] - end - - @testset "test identity map" begin - ex = :(a+b+2 * 2 + foo(2)) - l11 = list(ex.args...) - @test collect(map(x->x, l11)) == collect(l11) - end - - @testset "test promotion" begin - l12 = list(Int(1), Float64(1)) - @test isa(l12, Cons{Float64}) - end - -end # @testset LinkedList diff --git a/test/test_mutable_list.jl b/test/test_mutable_list.jl deleted file mode 100644 index d3bf7491..00000000 --- a/test/test_mutable_list.jl +++ /dev/null @@ -1,206 +0,0 @@ -@testset "MutableLinkedList" begin - - @testset "empty list" begin - l1 = MutableLinkedList{Int}() - @test MutableLinkedList() == MutableLinkedList{Any}() - @test iterate(l1) === nothing - @test isempty(l1) - @test length(l1) == 0 - @test lastindex(l1) == 0 - @test collect(l1) == Int[] - @test eltype(l1) == Int - @test eltype(typeof(l1)) == Int - @test_throws ArgumentError pop!(l1) - @test_throws ArgumentError popfirst!(l1) - end - - @testset "core functionality" begin - n = 10 - - @testset "push back / pop back" begin - l = MutableLinkedList{Int}() - - @testset "push back" begin - for i = 1:n - push!(l, i) - @test last(l) == i - if i > 4 - @test getindex(l, i) == i - @test getindex(l, 1:floor(Int, i/2)) == MutableLinkedList{Int}(1:floor(Int, i/2)...) - @test l[1:floor(Int, i/2)] == MutableLinkedList{Int}(1:floor(Int, i/2)...) - setindex!(l, 0, i - 2) - @test l == MutableLinkedList{Int}(1:i-3..., 0, i-1:i...) - setindex!(l, i - 2, i - 2) - end - @test lastindex(l) == i - @test length(l) == i - @test isempty(l) == false - for (j, k) in enumerate(l) - @test j == k - end - if i > 3 - l1 = MutableLinkedList{Int32}(1:i...) - io = IOBuffer() - @test sprint(io -> show(io, iterate(l1))) == "(1, DataStructures.ListNode{Int32}(2))" - @test sprint(io -> show(io, iterate(l1, l1.node.next.next))) == "(2, DataStructures.ListNode{Int32}(3))" - end - cl = collect(l) - @test isa(cl, Vector{Int}) - @test cl == collect(1:i) - end - end - - @testset "pop back" begin - for i = 1:n - x = pop!(l) - @test length(l) == n - i - @test isempty(l) == (i == n) - @test x == n - i + 1 - cl = collect(l) - @test cl == collect(1:n-i) - end - end - end - - @testset "push front / pop front" begin - l = MutableLinkedList{Int}() - - @testset "push front" begin - for i = 1:n - pushfirst!(l, i) - @test first(l) == i - @test length(l) == i - @test isempty(l) == false - cl = collect(l) - @test isa(cl, Vector{Int}) - @test cl == collect(i:-1:1) - end - end - - @testset "pop front" begin - for i = 1:n - x = popfirst!(l) - @test length(l) == n - i - @test isempty(l) == (i == n) - @test x == n - i + 1 - cl = collect(l) - @test cl == collect(n-i:-1:1) - end - end - - end - - @testset "append / delete / copy / reverse" begin - for i = 1:n - l = MutableLinkedList{Int}(1:n...) - - @testset "append" begin - l2 = MutableLinkedList{Int}(n+1:2n...) - append!(l, l2) - @test l == MutableLinkedList{Int}(1:2n...) - @test l2 == MutableLinkedList{Int}() - @test collect(l) == collect(MutableLinkedList{Int}(1:2n...)) - l3 = MutableLinkedList{Int}(1:n...) - append!(l3, n+1:2n) - @test l3 == MutableLinkedList{Int}(1:2n...) - @test collect(l3) == collect(MutableLinkedList{Int}(1:2n...)) - l4 = MutableLinkedList{Int}(1:n...) - push!(l4, n+1:2n...) - @test l4 == MutableLinkedList{Int}(1:2n...) - @test collect(l4) == collect(MutableLinkedList{Int}(1:2n...)) - end - - @testset "delete" begin - delete!(l, n+1:2n) - @test l == MutableLinkedList{Int}(1:n...) - for i = n:-1:1 - delete!(l, i) - end - @test l == MutableLinkedList{Int}() - l = MutableLinkedList{Int}(1:n...) - @test_throws BoundsError delete!(l, n-1:2n) - @test_throws BoundsError delete!(l, 2n) - end - - @testset "copy" begin - l2 = copy(l) - @test l == l2 - end - - @testset "reverse" begin - l2 = MutableLinkedList{Int}(n:-1:1...) - @test l == reverse(l2) - end - end - end - - @testset "map / filter" begin - for i = 1:n - @testset "map" begin - l = MutableLinkedList{Int}(1:n...) - @test map(x -> 2x, l) == MutableLinkedList{Int}(2:2:2n...) - l2 = MutableLinkedList{Float64}() - @test map(x -> x*im, l2) == MutableLinkedList{Complex{Float64}}() - @test map(Int32, l2) == MutableLinkedList{Int32}() - f(x) = x % 2 == 0 ? convert(Int8, x) : convert(Float16, x) - @test typeof(map(f, l)) == MutableLinkedList{Real} - end - - @testset "filter" begin - l = MutableLinkedList{Int}(1:n...) - @test filter(x -> x % 2 == 0, l) == MutableLinkedList{Int}(2:2:n...) - end - - @testset "show" begin - l = MutableLinkedList{Int32}(1:n...) - io = IOBuffer() - @test sprint(io -> show(io, l.node.next)) == "$(typeof(l.node.next))($(l.node.next.data))" - io1 = IOBuffer() - write(io1, "MutableLinkedList{Int32}("); - write(io1, join(l, ", ")); - write(io1, ")") - seekstart(io1) - @test sprint(io -> show(io, l)) == read(io1, String) - end - end - end - end - - @testset "random operations" begin - l = MutableLinkedList{Int}() - r = Int[] - m = 100 - - for k = 1 : m - la = rand(1:20) - x = rand(1:1000, la) - - for i = 1 : la - if rand(Bool) - push!(r, x[i]) - push!(l, x[i]) - else - pushfirst!(r, x[i]) - pushfirst!(l, x[i]) - end - end - - @test length(l) == length(r) - @test collect(l) == r - - lr = rand(1:length(r)) - for i = 1 : lr - if rand(Bool) - pop!(r) - pop!(l) - else - popfirst!(r) - popfirst!(l) - end - end - - @test length(l) == length(r) - @test collect(l) == r - end - end -end