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..b85f40b 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,22 @@ 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 + + +## X => a / X => (a, as...) syntax as an alternative to make(X, a) / make(X, a, as...) + +# this is experimental + +@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