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..c20f0ed 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,23 @@ 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 + +pair_to_make((a, b)::Pair) = + b isa Tuple ? + make(a, map(pair_to_make, b)...) : + make(a, pair_to_make(b)) + +pair_to_make(x) = x + +@inline Sampler(RNG::Type{<:AbstractRNG}, p::Pair, r::Repetition) = + Sampler(RNG, pair_to_make(p), r) + +# nothing can be inferred when only the pair type is available +@inline gentype(::Type{<:Pair}) = Any + +@inline gentype(p::Pair) = gentype(pair_to_make(p)) diff --git a/test/runtests.jl b/test/runtests.jl index 95709e9..4912be8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -280,11 +280,13 @@ end @test fieldtype(typeof(a), 2) == UInt64 end +#= @testset "rand(::Pair)" begin @test rand(1=>3) ∈ (1, 3) @test rand(1=>2, 3) isa Vector{Int} @test rand(1=>'2', 3) isa Vector{Union{Char, Int}} end +=# @testset "rand(::AbstractFloat)" begin # check that overridden methods still work @@ -565,6 +567,19 @@ 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 MethodError rand(1=>2) # calls 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}} + + nt = rand(NTuple{4} => Complex => 1:3) + @test nt isa NTuple{4,Complex{Int64}} +end + + ## @rand struct Die