Skip to content

Commit

Permalink
Merge pull request #168 from JuliaLang/kms/sorted_collections_default…
Browse files Browse the repository at this point in the history
…_constructors

RFC: Add default 2-type argument constructors to sorted collections
  • Loading branch information
kmsquire committed Jan 25, 2017
2 parents 8ea26d1 + 25d9aec commit 6846f65
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 250 deletions.
77 changes: 43 additions & 34 deletions doc/source/sorted_containers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SortedDict, SortedMultiDict and SortedSet.
*SortedDict* is similar to the built-in Julia type ``Dict``
with the additional feature that the keys are stored in
sorted order and can be efficiently iterated in this order.
SortedDict is a subtype of Associative. It is slower than ``Dict``
SortedDict is a subtype of Associative. It is generally slower than ``Dict``
because looking up a key requires an O(log *n*) tree search rather than
an expected O(1) hash-table lookup time as with Dict.
SortedDict is
Expand Down Expand Up @@ -114,49 +114,58 @@ in terms of the container.
Constructors for Sorted Containers
~~~~~~~~~~~~~~~~~~~~~~~~~~~

``SortedDict(d)``
Argument ``d`` is an ordinary Julia dict (or any associative type)
used to initialize the container::
``SortedDict(o=Forward)``
``SortedDict{K,V}(o=Forward)``
Construct an empty ``SortedDict`` with key type ``K`` and value type ``V``.
If ``K`` and ``V`` are not specified, the dictionary defaults to a
``SortedDict{Any,Any}``. Keys and values are converted to the
given type upon insertion.
Ordering ``o`` defaults to ``Forward`` ordering.

c = SortedDict(Dict("New York" => 1788, "Illinois" => 1818))
**Note that a key type of ``Any`` will lead to slow performance, as the
values are stored boxed (i.e., as pointers), and insertion will
require a run-time lookup of the appropriate comparison function.
It is recommended to always specify a key type, or to use one of the
constructors below in which the key type is inferred.**

``SortedDict(k1=>v1, k2=>v2, ...)``
``SortedDict{K,V}(k1=>v1, k2=>v2, ...)``
Construct a ``SortedDict`` from the given key-value pairs.
If ``K`` and ``V`` are not specified, key type and
value type are inferred from the given key-value pairs, and ordering is assumed
to be ``Forward`` ordering.

In this example the key-type is deduced to be String, while the
value-type is Int. The default ordering object ``Forward`` is used.
``SortedDict(o, k1=>v1, k2=>v2, ...)``
``SortedDict{K,V}(o, k1=>v1, k2=>v2, ...)``
Construct a ``SortedDict`` from the given pairs with the specified
ordering ``o``. If ``K`` and ``V`` are
not specified, the key type and value type are inferred from the given pairs.
See below for more information about ordering.

``SortedDict(d,o)``
Argument ``d`` is an ordinary Julia dict (or any associative type)
used to initialize the container and ``o`` is an ordering object
used for ordering the keys.
``SortedDict(d,o=Forward)``
``SortedDict{K,V}(d,o=Forward)``
Construct a ``SortedDict`` from an ordinary Julia dict ``d`` (or
any associative type), e.g.::

``SortedDict(k1=>v1, k2=>v2, ...)``
Arguments are key-value pairs for insertion into the
dictionary.
The keys must be of the same type as one another; the
values must also be of one type.
d = Dict("New York" => 1788, "Illinois" => 1818)
c = SortedDict(d)

``SortedDict(o, k1=>v1, k2=>v2, ...)``
The first argument ``o`` is an ordering object. The remaining
arguments are key-value pairs for insertion into the
dictionary.
The keys must be of the same type as one another; the
values must also be of one type.
In this example the key-type is deduced to be ``String``, while the
value-type is ``Int``.

``SortedDict(iter)``
Takes an arbitrary iterable object of key=>value pairs.
The default Forward ordering is used.
If ``K`` and ``V`` are not specified, the key type and value type are inferred
from the given dictionary.
The ordering object ``o`` defaults to ``Forward``.

``SortedDict(iter,o)``
Takes an arbitrary iterable object of key=>value pairs.
The ordering object ``o`` is explicitly given.
See below for more information about ordering.

``SortedDict{K,V,Ord}(o)``
Construct an empty SortedDict in which type parameters
are explicitly listed; ordering object is explicitly specified.
(See below for discussion of ordering.) An empty SortedDict
may also be constructed using ``SortedDict(Dict{K,V}(),o)``
where the ``o`` argument is optional.
``SortedDict(iter,o=Forward)``
``SortedDict{K,V}(iter,o=Forward)``
Construct a ``SortedDict`` from an arbitrary iterable object of
``key=>value`` pairs.
If ``K`` and ``V`` are not specified, the key type and value type are inferred
from the given iterable.
The ordering object ``o`` defaults to ``Forward``.

``SortedMultiDict(ks,vs,o)``
Construct a SortedMultiDict using keys given by ``ks``, values
Expand Down
127 changes: 74 additions & 53 deletions src/sorted_dict.jl
Original file line number Diff line number Diff line change
@@ -1,81 +1,102 @@
## A SortedDict is a wrapper around balancedTree with
## methods similiar to those of Julia container Dict.


type SortedDict{K, D, Ord <: Ordering} <: Associative{K,D}
bt::BalancedTree23{K,D,Ord}

## Zero-argument constructor, or possibly one argument to specify order.
## Base constructors

function SortedDict(o::Ord=Forward)
bt1 = BalancedTree23{K,D,Ord}(o)
new(bt1)
end
SortedDict(o::Ord) = new(BalancedTree23{K,D,Ord}(o))

end
function SortedDict(o::Ord, kv)
s = new(BalancedTree23{K,D,Ord}(o))

## external constructor to take an associative and infer
## argument types

function SortedDict{K, D, Ord <: Ordering}(d::Associative{K,D}, o::Ord=Forward)
h = SortedDict{K,D,Ord}(o)
for (k,v) in d
h[k] = v
if eltype(kv) <: Pair
# It's (possibly?) more efficient to access the first and second
# elements of Pairs directly, rather than destructure
for p in kv
s[p.first] = p.second
end
else
for (k,v) in kv
s[k] = v
end
end
return s
end
h
end



## More constructors based on those in dict.jl:
## Take pairs and infer argument
## types. Note: this works only for the Forward ordering.

function SortedDict{K,D}(ps::Pair{K,D}...)
h = SortedDict{K,D,ForwardOrdering}()
for p in ps
h[p.first] = p.second
end
h
end

# Any-Any constructors
SortedDict() = SortedDict{Any,Any,ForwardOrdering}(Forward)
SortedDict{Ord <: Ordering}(o::Ord) = SortedDict{Any,Any,Ord}(o)

## Take pairs and infer argument
## types. Ordering parameter must be explicit first argument.

function not_iterator_of_pairs(kv)
return any(x->isempty(methodswith(typeof(kv), x, true)),
[start, next, done]) ||
any(x->!isa(x, Union{Tuple,Pair}), kv)
end

function SortedDict{K,D, Ord <: Ordering}(o::Ord, ps::Pair{K,D}...)
h = SortedDict{K,D,Ord}(o)
for p in ps
h[p.first] = p.second
# Construction from Pairs
# TODO: fix SortedDict(1=>1, 2=>2.0)
SortedDict(ps::Pair...) = SortedDict(Forward, ps)
SortedDict(o::Ordering, ps::Pair...) = SortedDict(o, ps)
@compat (::Type{SortedDict{K,D}}){K,D}(ps::Pair...) = SortedDict{K,D,ForwardOrdering}(Forward, ps)
@compat (::Type{SortedDict{K,D}}){K,D,Ord<:Ordering}(o::Ord, ps::Pair...) = SortedDict{K,D,Ord}(o, ps)

# Construction from Associatives
SortedDict{K,D,Ord<:Ordering}(o::Ord, d::Associative{K,D}) = SortedDict{K,D,Ord}(o, d)

## Construction from iteratables of Pairs/Tuples

# Construction specifying Key/Value types
# e.g., SortedDict{Int,Float64}([1=>1, 2=>2.0])
@compat (::Type{SortedDict{K,D}}){K,D}(kv) = SortedDict{K,D}(Forward, kv)
@compat function (::Type{SortedDict{K,D}}){K,D,Ord<:Ordering}(o::Ord, kv)
try
SortedDict{K,D,Ord}(o, kv)
catch e
if not_iterator_of_pairs(kv)
throw(ArgumentError("SortedDict(kv): kv needs to be an iterator of tuples or pairs"))
else
rethrow(e)
end
end
h
end

## This one takes an iterable; ordering type is optional.

SortedDict{Ord <: Ordering}(kv, o::Ord=Forward) =
sorteddict_with_eltype(kv, eltype(kv), o)

function sorteddict_with_eltype{K,D,Ord}(kv, ::Type{Pair{K,D}}, o::Ord)
h = SortedDict{K,D,Ord}(o)
for (k,v) in kv
h[k] = v
# Construction inferring Key/Value types from input
# e.g. SortedDict{}
SortedDict(kv, o::Ordering=Forward) = SortedDict(o, kv)
function SortedDict(o::Ordering, kv)
try
_sorted_dict_with_eltype(o, kv, eltype(kv))
catch e
if not_iterator_of_pairs(kv)
throw(ArgumentError("SortedDict(kv): kv needs to be an iterator of tuples or pairs"))
else
rethrow(e)
end
end
h
end

_sorted_dict_with_eltype{K,D,Ord}(o::Ord, ps, ::Type{Pair{K,D}}) = SortedDict{ K, D,Ord}(o, ps)
_sorted_dict_with_eltype{K,D,Ord}(o::Ord, kv, ::Type{Tuple{K,D}}) = SortedDict{ K, D,Ord}(o, kv)
_sorted_dict_with_eltype{K, Ord}(o::Ord, ps, ::Type{Pair{K}} ) = SortedDict{ K,Any,Ord}(o, ps)
_sorted_dict_with_eltype{ Ord}(o::Ord, kv, ::Type ) = SortedDict{Any,Any,Ord}(o, kv)


## TODO: It seems impossible (or at least very challenging) to create the eltype below.
## If deemed possible, please create a test and uncomment this definition.
# if VERSION < v"0.6.0-dev.2123"
# _sorted_dict_with_eltype{ D,Ord}(o::Ord, ps, ::Type{Pair{TypeVar(:K),D}}) = SortedDict{Any, D,Ord}(o, ps)
# else
# eval(parse("_sorted_dict_with_eltype{ D,Ord}(o::Ord, ps, ::Type{Pair{K,D} where K}) = SortedDict{Any, D,Ord}(o, ps)"))
# end


typealias SDSemiToken IntSemiToken

typealias SDToken Tuple{SortedDict,IntSemiToken}




## This function implements m[k]; it returns the
## data item associated with key k.

Expand All @@ -97,8 +118,8 @@ end
## push! is an alternative to insert!; it returns the container.


@inline function push!{K, D, Ord <: Ordering}(m::SortedDict{K,D,Ord}, pr::Pair{K,D})
insert!(m.bt, convert(K,pr[1]), convert(D,pr[2]), false)
@inline function push!{K,D}(m::SortedDict{K,D}, pr::Pair)
insert!(m.bt, convert(K, pr[1]), convert(D, pr[2]), false)
m
end

Expand Down Expand Up @@ -290,4 +311,4 @@ end


similar{K,D,Ord<:Ordering}(m::SortedDict{K,D,Ord}) =
SortedDict{K,D,Ord}(orderobject(m))
SortedDict{K,D,Ord}(orderobject(m))
8 changes: 7 additions & 1 deletion src/sorted_multi_dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ type SortedMultiDict{K, D, Ord <: Ordering}
end
end

@compat (::Type{SortedMultiDict})() = SortedMultiDict{Any,Any,ForwardOrdering}(Forward)
@compat (::Type{SortedMultiDict{K,D}}){K,D}() = SortedMultiDict{K,D,ForwardOrdering}(Forward)
@compat (::Type{SortedMultiDict}){O<:Ordering}(o::O) = SortedMultiDict{Any,Any,O}(o)
@compat (::Type{SortedMultiDict{K,D}}){K,D,O<:Ordering}(o::O) = SortedMultiDict{K,D,O}(o)
# @compat (::Type{SortedMultiDict{K,D}}){K,D,O<:Ordering}(o::O, ps::Pair...) = SortedMultiDict{K,D,O}(o, ps...)
# @compat (::Type{SortedMultiDict{K,D}}){K,D}(ps::Pair...) = SortedMultiDict{K,D}(Base.Forward, ps...)

typealias SMDSemiToken IntSemiToken

Expand Down Expand Up @@ -87,7 +93,7 @@ end
## push! is an alternative to insert!; it returns the container.


@inline function push!{K, D, Ord <: Ordering}(m::SortedMultiDict{K,D,Ord}, pr::Pair{K,D})
@inline function push!{K,D}(m::SortedMultiDict{K,D}, pr::Pair)
insert!(m.bt, convert(K,pr[1]), convert(D,pr[2]), true)
m
end
Expand Down
7 changes: 7 additions & 0 deletions src/sorted_set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ type SortedSet{K, Ord <: Ordering}
end
end

@compat (::Type{SortedSet})() = SortedSet{Any,ForwardOrdering}(Forward)
@compat (::Type{SortedSet{K}}){K}() = SortedSet{K,ForwardOrdering}(Forward)
@compat (::Type{SortedSet}){O<:Ordering}(o::O) = SortedSet{Any,O}(o)
@compat (::Type{SortedSet{K}}){K,O<:Ordering}(o::O) = SortedSet{K,O}(o)
# @compat (::Type{SortedSet{K}}){K,O<:Ordering}(o::O, ps...) = SortedSet{K,O}(o, ps...)
# @compat (::Type{SortedSet{K}}){K}(ps...) = SortedSet{K}(Base.Forward, ps...)


typealias SetSemiToken IntSemiToken

Expand Down
Loading

0 comments on commit 6846f65

Please sign in to comment.