diff --git a/NEWS.md b/NEWS.md index 5fd6406725e68..99886086171d3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -78,6 +78,9 @@ This section lists changes that do not have deprecation warnings. * `quadgk` has been moved from Base into a separate package. ([#19741]) + * The `Collections` module has been removed, and all functions defined therein have been + moved to the `DataStructures` package. ([#19800]) + * The `RepString` type has been moved to the [LegacyStrings.jl package](https://github.com/JuliaArchive/LegacyStrings.jl). diff --git a/base/array.jl b/base/array.jl index 2946e6022ad0b..f5bc45fe00af5 100644 --- a/base/array.jl +++ b/base/array.jl @@ -560,6 +560,24 @@ function append!{T}(a::Array{T,1}, items::AbstractVector) return a end +append!(a::Vector, iter) = _append!(a, iteratorsize(iter), iter) + +function _append!(a, ::HasLength, iter) + n = length(a) + resize!(a, n+length(iter)) + @inbounds for (i,item) in zip(n+1:length(a), iter) + a[i] = item + end + a +end + +function _append!(a, ::IteratorSize, iter) + for item in iter + push!(a, item) + end + a +end + """ prepend!(a::Vector, items) -> collection diff --git a/base/collections.jl b/base/collections.jl deleted file mode 100644 index 43fd55f7f803c..0000000000000 --- a/base/collections.jl +++ /dev/null @@ -1,424 +0,0 @@ -# This file is a part of Julia. License is MIT: http://julialang.org/license - -module Collections - -import Base: setindex!, done, get, hash, haskey, isempty, length, next, getindex, start, copymutable -import ..Order: Forward, Ordering, lt - -export - PriorityQueue, - dequeue!, - enqueue!, - heapify!, - heapify, - heappop!, - heappush!, - isheap, - peek - - -# Some algorithms that can be defined only after infrastructure is in place -Base.append!(a::Vector, iter) = _append!(a, Base.iteratorsize(iter), iter) - -function _append!(a, ::Base.HasLength, iter) - n = length(a) - resize!(a, n+length(iter)) - @inbounds for (i,item) in zip(n+1:length(a), iter) - a[i] = item - end - a -end - -function _append!(a, ::Base.IteratorSize, iter) - for item in iter - push!(a, item) - end - a -end - -# Heap operations on flat arrays -# ------------------------------ - - -# Binary heap indexing -heapleft(i::Integer) = 2i -heapright(i::Integer) = 2i + 1 -heapparent(i::Integer) = div(i, 2) - - -# Binary min-heap percolate down. -function percolate_down!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward, len::Integer=length(xs)) - @inbounds while (l = heapleft(i)) <= len - r = heapright(i) - j = r > len || lt(o, xs[l], xs[r]) ? l : r - if lt(o, xs[j], x) - xs[i] = xs[j] - i = j - else - break - end - end - xs[i] = x -end - -percolate_down!(xs::AbstractArray, i::Integer, o::Ordering, len::Integer=length(xs)) = percolate_down!(xs, i, xs[i], o, len) - - -# Binary min-heap percolate up. -function percolate_up!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward) - @inbounds while (j = heapparent(i)) >= 1 - if lt(o, x, xs[j]) - xs[i] = xs[j] - i = j - else - break - end - end - xs[i] = x -end - -percolate_up!{T}(xs::AbstractArray{T}, i::Integer, o::Ordering) = percolate_up!(xs, i, xs[i], o) - -""" - heappop!(v, [ord]) - -Given a binary heap-ordered array, remove and return the lowest ordered element. -For efficiency, this function does not check that the array is indeed heap-ordered. -""" -function heappop!(xs::AbstractArray, o::Ordering=Forward) - x = xs[1] - y = pop!(xs) - if !isempty(xs) - percolate_down!(xs, 1, y, o) - end - x -end - -""" - heappush!(v, x, [ord]) - -Given a binary heap-ordered array, push a new element `x`, preserving the heap property. -For efficiency, this function does not check that the array is indeed heap-ordered. -""" -function heappush!(xs::AbstractArray, x, o::Ordering=Forward) - push!(xs, x) - percolate_up!(xs, length(xs), x, o) - xs -end - - -# Turn an arbitrary array into a binary min-heap in linear time. -""" - heapify!(v, ord::Ordering=Forward) - -In-place [`heapify`](@ref). -""" -function heapify!(xs::AbstractArray, o::Ordering=Forward) - for i in heapparent(length(xs)):-1:1 - percolate_down!(xs, i, o) - end - xs -end - -""" - heapify(v, ord::Ordering=Forward) - -Returns a new vector in binary heap order, optionally using the given ordering. -```jldoctest -julia> a = [1,3,4,5,2]; - -julia> Base.Collections.heapify(a) -5-element Array{Int64,1}: - 1 - 2 - 4 - 5 - 3 - -julia> Base.Collections.heapify(a, Base.Order.Reverse) -5-element Array{Int64,1}: - 5 - 3 - 4 - 1 - 2 -``` -""" -heapify(xs::AbstractArray, o::Ordering=Forward) = heapify!(copymutable(xs), o) - -""" - isheap(v, ord::Ordering=Forward) - -Return `true` if an array is heap-ordered according to the given order. - -```jldoctest -julia> a = [1,2,3] -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> Base.Collections.isheap(a,Base.Order.Forward) -true - -julia> Base.Collections.isheap(a,Base.Order.Reverse) -false -``` -""" -function isheap(xs::AbstractArray, o::Ordering=Forward) - for i in 1:div(length(xs), 2) - if lt(o, xs[heapleft(i)], xs[i]) || - (heapright(i) <= length(xs) && lt(o, xs[heapright(i)], xs[i])) - return false - end - end - true -end - - -# PriorityQueue -# ------------- - -""" - PriorityQueue(K, V, [ord]) - -Construct a new [`PriorityQueue`](@ref), with keys of type -`K` and values/priorites of type `V`. -If an order is not given, the priority queue is min-ordered using -the default comparison for `V`. - -A `PriorityQueue` acts like a `Dict`, mapping values to their -priorities, with the addition of a `dequeue!` function to remove the -lowest priority element. - -```jldoctest -julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) -Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: - "c" => 1 - "b" => 3 - "a" => 2 -``` -""" -type PriorityQueue{K,V,O<:Ordering} <: Associative{K,V} - # Binary heap of (element, priority) pairs. - xs::Array{Pair{K,V}, 1} - o::O - - # Map elements to their index in xs - index::Dict{K, Int} - - function PriorityQueue(o::O) - new(Array{Pair{K,V}}(0), o, Dict{K, Int}()) - end - - PriorityQueue() = PriorityQueue{K,V,O}(Forward) - - function PriorityQueue(ks::AbstractArray{K}, vs::AbstractArray{V}, - o::O) - # TODO: maybe deprecate - if length(ks) != length(vs) - throw(ArgumentError("key and value arrays must have equal lengths")) - end - PriorityQueue{K,V,O}(zip(ks, vs), o) - end - - function PriorityQueue(itr, o::O) - xs = Array{Pair{K,V}}(length(itr)) - index = Dict{K, Int}() - for (i, (k, v)) in enumerate(itr) - xs[i] = Pair{K,V}(k, v) - if haskey(index, k) - throw(ArgumentError("PriorityQueue keys must be unique")) - end - index[k] = i - end - pq = new(xs, o, index) - - # heapify - for i in heapparent(length(pq.xs)):-1:1 - percolate_down!(pq, i) - end - - pq - end -end - -PriorityQueue(o::Ordering=Forward) = PriorityQueue{Any,Any,typeof(o)}(o) -PriorityQueue{K,V}(::Type{K}, ::Type{V}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(o) - -# TODO: maybe deprecate -PriorityQueue{K,V}(ks::AbstractArray{K}, vs::AbstractArray{V}, - o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(ks, vs, o) - -PriorityQueue{K,V}(kvs::Associative{K,V}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(kvs, o) - -PriorityQueue{K,V}(a::AbstractArray{Tuple{K,V}}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(a, o) - -length(pq::PriorityQueue) = length(pq.xs) -isempty(pq::PriorityQueue) = isempty(pq.xs) -haskey(pq::PriorityQueue, key) = haskey(pq.index, key) - -""" - peek(pq) - -Return the lowest priority key from a priority queue without removing that -key from the queue. -""" -peek(pq::PriorityQueue) = pq.xs[1] - -function percolate_down!(pq::PriorityQueue, i::Integer) - x = pq.xs[i] - @inbounds while (l = heapleft(i)) <= length(pq) - r = heapright(i) - j = r > length(pq) || lt(pq.o, pq.xs[l].second, pq.xs[r].second) ? l : r - if lt(pq.o, pq.xs[j].second, x.second) - pq.index[pq.xs[j].first] = i - pq.xs[i] = pq.xs[j] - i = j - else - break - end - end - pq.index[x.first] = i - pq.xs[i] = x -end - - -function percolate_up!(pq::PriorityQueue, i::Integer) - x = pq.xs[i] - @inbounds while i > 1 - j = heapparent(i) - if lt(pq.o, x.second, pq.xs[j].second) - pq.index[pq.xs[j].first] = i - pq.xs[i] = pq.xs[j] - i = j - else - break - end - end - pq.index[x.first] = i - pq.xs[i] = x -end - -# Equivalent to percolate_up! with an element having lower priority than any other -function force_up!(pq::PriorityQueue, i::Integer) - x = pq.xs[i] - @inbounds while i > 1 - j = heapparent(i) - pq.index[pq.xs[j].first] = i - pq.xs[i] = pq.xs[j] - i = j - end - pq.index[x.first] = i - pq.xs[i] = x -end - -function getindex{K,V}(pq::PriorityQueue{K,V}, key) - pq.xs[pq.index[key]].second -end - - -function get{K,V}(pq::PriorityQueue{K,V}, key, deflt) - i = get(pq.index, key, 0) - i == 0 ? deflt : pq.xs[i].second -end - - -# Change the priority of an existing element, or equeue it if it isn't present. -function setindex!{K,V}(pq::PriorityQueue{K, V}, value, key) - if haskey(pq, key) - i = pq.index[key] - oldvalue = pq.xs[i].second - pq.xs[i] = Pair{K,V}(key, value) - if lt(pq.o, oldvalue, value) - percolate_down!(pq, i) - else - percolate_up!(pq, i) - end - else - enqueue!(pq, key, value) - end - value -end - -""" - enqueue!(pq, k, v) - -Insert the a key `k` into a priority queue `pq` with priority `v`. - -```jldoctest -julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) -Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: - "c" => 1 - "b" => 3 - "a" => 2 - -julia> Base.Collections.enqueue!(a, "d", 4) -Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 4 entries: - "c" => 1 - "b" => 3 - "a" => 2 - "d" => 4 -``` -""" -function enqueue!{K,V}(pq::PriorityQueue{K,V}, key, value) - if haskey(pq, key) - throw(ArgumentError("PriorityQueue keys must be unique")) - end - push!(pq.xs, Pair{K,V}(key, value)) - pq.index[key] = length(pq) - percolate_up!(pq, length(pq)) - pq -end - -""" - dequeue!(pq) - -Remove and return the lowest priority key from a priority queue. - -```jldoctest -julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) -Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: - "c" => 1 - "b" => 3 - "a" => 2 - -julia> Base.Collections.dequeue!(a) -"c" - -julia> a -Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 2 entries: - "b" => 3 - "a" => 2 -``` -""" -function dequeue!(pq::PriorityQueue) - x = pq.xs[1] - y = pop!(pq.xs) - if !isempty(pq) - pq.xs[1] = y - pq.index[y.first] = 1 - percolate_down!(pq, 1) - end - delete!(pq.index, x.first) - x.first -end - -function dequeue!(pq::PriorityQueue, key) - idx = pq.index[key] - force_up!(pq, idx) - dequeue!(pq) - key -end - -# Unordered iteration through key value pairs in a PriorityQueue -start(pq::PriorityQueue) = start(pq.index) - -done(pq::PriorityQueue, i) = done(pq.index, i) - -function next{K,V}(pq::PriorityQueue{K,V}, i) - (k, idx), i = next(pq.index, i) - return (pq.xs[idx], i) -end - -end # module Collections diff --git a/base/deprecated.jl b/base/deprecated.jl index de7659aaa52b0..a0634bbe80412 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1349,6 +1349,19 @@ function quadgk(args...; kwargs...) end export quadgk +# Collections functions moved to a package (#19800) +module Collections + export PriorityQueue, enqueue!, dequeue!, heapify!, heapify, heappop!, heappush!, isheap, peek + for f in (:PriorityQueue, :enqueue!, :dequeue!, :heapify!, :heapify, :heappop!, :heappush!, :isheap, :peek) + @eval function ($f)(args...; kwargs...) + error(string($f, args, " has been moved to the package DataStructures.jl.\n", + "Run Pkg.add(\"DataStructures\") to install DataStructures on Julia v0.6 and later, ", + "and then run `using DataStructures`.")) + end + end +end +export Collections + # Broadcast now returns a BitArray when the resulting eltype is Bool (#17623) @deprecate bitbroadcast broadcast diff --git a/base/exports.jl b/base/exports.jl index d51dac70d081a..c9be9f24ad702 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -2,7 +2,6 @@ export # Modules - Collections, FFTW, Meta, Operators, diff --git a/base/sysimg.jl b/base/sysimg.jl index 31fd743b4ef42..12a051bec0e37 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -244,7 +244,6 @@ include("reducedim.jl") # macros in this file relies on string.jl # basic data structures include("ordering.jl") importall .Order -include("collections.jl") # Combinatorics include("sort.jl") diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index bf25a4f9f0c99..bc8213823a6d8 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -260,53 +260,3 @@ Fully implemented by: * `Vector` (a.k.a. 1-dimensional [`Array`](@ref)) * `BitVector` (a.k.a. 1-dimensional [`BitArray`](@ref)) - -## PriorityQueue - -The [`PriorityQueue`](@ref Base.Collections.PriorityQueue) type is available from the `Collections` module. It provides a basic -priority queue implementation allowing for arbitrary key and priority types. Multiple identical -keys are not permitted, but the priority of existing keys can be changed efficiently. - -```@docs -Base.Collections.PriorityQueue -Base.Collections.enqueue! -Base.Collections.dequeue! -Base.Collections.peek -``` - -[`PriorityQueue`](@ref Base.Collections.PriorityQueue) also behaves similarly to a `Dict` in that keys can be inserted and priorities -accessed or changed using indexing notation. - -```julia -julia> # Julia code - pq = Collections.PriorityQueue(); - -julia> # Insert keys with associated priorities - pq["a"] = 10; pq["b"] = 5; pq["c"] = 15; pq -Base.Collections.PriorityQueue{Any,Any,Base.Order.ForwardOrdering} with 3 entries: - "c" => 15 - "b" => 5 - "a" => 10 - -julia> # Change the priority of an existing key - pq["a"] = 0; pq -Base.Collections.PriorityQueue{Any,Any,Base.Order.ForwardOrdering} with 3 entries: - "c" => 15 - "b" => 5 - "a" => 0 -``` - -## Heap Functions - -Along with the [`PriorityQueue`](@ref Base.Collections.PriorityQueue) type, the `Collections` module provides lower level functions -for performing binary heap operations on arrays. Each function takes an optional ordering argument. -If not given, default ordering is used, so that elements popped from the heap are given in ascending -order. - -```@docs -Base.Collections.heapify -Base.Collections.heapify! -Base.Collections.isheap -Base.Collections.heappush! -Base.Collections.heappop! -``` diff --git a/test/choosetests.jl b/test/choosetests.jl index bd1fc5efcf72c..cb1f045e72aa1 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -24,7 +24,7 @@ function choosetests(choices = []) "bitarray", "copy", "math", "fastmath", "functional", "iterators", "operators", "path", "ccall", "parse", "loading", "bigint", "bigfloat", "sorting", "statistics", "spawn", "backtrace", - "priorityqueue", "file", "read", "mmap", "version", "resolve", + "file", "read", "mmap", "version", "resolve", "pollfd", "mpfr", "broadcast", "complex", "socket", "floatapprox", "datafmt", "reflection", "regex", "float16", "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", diff --git a/test/priorityqueue.jl b/test/priorityqueue.jl deleted file mode 100644 index 7ff7a89d76766..0000000000000 --- a/test/priorityqueue.jl +++ /dev/null @@ -1,112 +0,0 @@ -# This file is a part of Julia. License is MIT: http://julialang.org/license - -using Base.Collections - -# Test dequeing in sorted order. -function test_issorted!(pq::PriorityQueue, priorities) - last = dequeue!(pq) - while !isempty(pq) - value = dequeue!(pq) - @test priorities[last] <= priorities[value] - value = last - end -end - -function test_isrequested!(pq::PriorityQueue, keys) - i = 0 - while !isempty(pq) - krqst = keys[i+=1] - krcvd = dequeue!(pq, krqst) - @test krcvd == krqst - end -end - -pmax = 1000 -n = 10000 -r = rand(1:pmax, n) -priorities = Dict(zip(1:n, r)) - -# building from a dict -pq = PriorityQueue(priorities) -test_issorted!(pq, priorities) - -pq = PriorityQueue(priorities) -test_isrequested!(pq, 1:n) - -# building from two lists -ks, vs = 1:n, rand(1:pmax, n) -pq = PriorityQueue(ks, vs) -priorities = Dict(zip(ks, vs)) -test_issorted!(pq, priorities) -pq = PriorityQueue(ks, vs) -lowpri = findmin(vs) -@test peek(pq)[2] == pq[ks[lowpri[2]]] - -# building from two lists - error throw -ks, vs = 1:n+1, rand(1:pmax, n) -@test_throws ArgumentError PriorityQueue(ks, vs) - -#enqueue error throw -ks, vs = 1:n, rand(1:pmax, n) -pq = PriorityQueue(ks, vs) -@test_throws ArgumentError enqueue!(pq, 1, 10) - -# enqueing via enqueue! -pq = PriorityQueue() -for (k, v) in priorities - enqueue!(pq, k, v) -end -test_issorted!(pq, priorities) - - -# enqueing via assign -pq = PriorityQueue() -for (k, v) in priorities - pq[k] = v -end -test_issorted!(pq, priorities) - - -# changing priorities -pq = PriorityQueue() -for (k, v) in priorities - pq[k] = v -end - -for _ in 1:n - k = rand(1:n) - v = rand(1:pmax) - pq[k] = v - priorities[k] = v -end - -test_issorted!(pq, priorities) - -# dequeuing -pq = PriorityQueue(priorities) -try - dequeue!(pq, 0) - error("should have resulted in KeyError") -catch ex - @test isa(ex, KeyError) -end -@test 10 == dequeue!(pq, 10) -while !isempty(pq) - @test 10 != dequeue!(pq) -end - -# low level heap operations -xs = heapify!([v for v in values(priorities)]) -@test issorted([heappop!(xs) for _ in length(priorities)]) - -xs = heapify(10:-1:1) -@test issorted([heappop!(xs) for _ in 1:10]) - -xs = Array{Int,1}(0) -for priority in values(priorities) - heappush!(xs, priority) -end -@test issorted([heappop!(xs) for _ in length(priorities)]) - -@test Base.Collections.isheap([1, 2, 3], Base.Order.Forward) -@test !Base.Collections.isheap([1, 2, 3], Base.Order.Reverse)