From e4716c63f57c4ba1bac0dd9e8e60b388d0b61766 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 4 Jun 2019 12:17:18 +0200 Subject: [PATCH] introduce a Pair API (alternative to make) --- README.md | 16 ++++++++++++++++ src/sampling.jl | 17 ++++++++++++++++- test/runtests.jl | 10 ++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11a07aa..d6c0811 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,16 @@ is omitted). For example, `rand(make(Array, 2, 3), 3)` creates an array of matri Of course, `make` is not necessary, in that the same can be achieved with an ad hoc `struct`, which in some cases is clearer (e.g. `Normal(m, s)` rather than something like `make(Float64, Val(:Normal), m, s)`). +As an experimental feature, the following alternative API is available: +- `rand(T => x)` is equivalent to `rand(make(T, x))` +- `rand(T => (x, y, ...))` is equivalent to `rand(make(T, x, y, ...))` + +This is for convenience only (it may be more readable), but may be less efficient due to the +fact that the type of a pair containing a type doesn't know this exact type (e.g. `Pair => Int` +has type `Pair{UnionAll,DataType}`), so `rand` can't infer the type of the generated value. +Thanks to inlining, the inferred types can however be sufficiently tight in some cases +(e.g. `rand(Complex => Int, 3)` is of type `Vector{Complex{Int64}}` instead of `Vector{Any}`). + Point 3) allows something like `rand(1:30, Set, 10)` to produce a `Set` of length `10` with values from `1:30`. The idea is that `rand([rng], [S], Cont, etc...)` should always be equivalent to `rand([rng], make(Cont, [S], etc...))`. This design goes somewhat against the trend in `Base` to create @@ -204,6 +214,12 @@ julia> collect(Iterators.take(Uniform(1:10), 3)) # distributions can be iterated 7 10 5 + +julia> rand(Complex => Int) # equivalent to rand(make(Complex, Int)) (experimental) +4610038282330316390 + 4899086469899572461im + +julia> rand(Pair => (String, Int8)) # equivalent to rand(make(Pair, String, Int8)) (experimental) +"ODNXIePK" => 4 ``` In some cases, the `Rand` iterator can provide efficiency gains compared to diff --git a/src/sampling.jl b/src/sampling.jl index d6a26a2..f4ec546 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -154,12 +154,14 @@ Sampler(RNG::Type{<:AbstractRNG}, c::Categorical, n::Repetition) = ## random elements from pairs +#= disabled in favor of a special meaning for pairs + Sampler(RNG::Type{<:AbstractRNG}, t::Pair, n::Repetition) = SamplerSimple(t, Sampler(RNG, Bool, n)) rand(rng::AbstractRNG, sp::SamplerSimple{<:Pair}) = @inbounds return sp[][1 + rand(rng, sp.data)] - +=# ## composite types @@ -559,3 +561,16 @@ let b = UInt8['0':'9';'A':'Z';'a':'z'], rand(rng::AbstractRNG, sp::SamplerTag{Cont{String}}) = String(rand(rng, sp.data.first, sp.data.second)) end + +@inline Sampler(RNG::Type{<:AbstractRNG}, (a, b)::Pair{<:Union{DataType,UnionAll}}, r::Repetition) = + b isa Tuple ? + Sampler(RNG, make(a, b...), r) : + Sampler(RNG, make(a, b), r) + +# nothing can be inferred when only the pair type is available +@inline gentype(::Type{<:Pair{<:Union{DataType,UnionAll}}}) = Any + +@inline gentype((a, b)::Pair{<:Union{DataType,UnionAll}}) = + b isa Tuple ? + gentype(make(a, b...)) : + gentype(make(a, b)) diff --git a/test/runtests.jl b/test/runtests.jl index 95709e9..36e3b72 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -565,6 +565,16 @@ end @test rand(make(Float64)) isa Float64 end +@testset "rand(T => x) & rand(T => (x, y, ...))" begin + @test rand(Complex => Int) isa Complex{Int} + @test rand(Pair => (String, Int8)) isa Pair{String,Int8} + @test_throws ArgumentError rand(1=>2) # should not call rand(make(1, 2)) + + @test rand(Complex => Int, 3) isa Vector{Complex{Int}} + @test rand(Pair => (String, Int8), Set, 3) isa Set{Pair{String,Int8}} +end + + ## @rand struct Die