Skip to content

Commit

Permalink
add rand(::String)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet committed Jun 5, 2017
1 parent a3c9bbd commit 27d49ff
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
40 changes: 39 additions & 1 deletion base/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ globalRNG() = GLOBAL_RNG
Pick a random element or array of random elements from the set of values specified by `S`; `S` can be
* an indexable collection (for example `1:n` or `['x','y','z']`), or
* a `Dict`, a `Set` or an `IntSet`, or
* an `AbstractString`, a `Dict`, a `Set` or an `IntSet`, or
* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for
integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating
point numbers;
Expand Down Expand Up @@ -683,6 +683,44 @@ end
rand{T}(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) = rand!(rng, Array{T}(dims), r)
rand(rng::AbstractRNG, r::AbstractArray, dims::Int...) = rand(rng, r, dims)

# rand from a string

rand(rng::AbstractRNG, s::AbstractString) = rand_iter(rng, s, Base.iteratorsize(s))
rand(s::AbstractString) = rand(GLOBAL_RNG, s)

## optimization for String

isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i))

function rand(rng::AbstractRNG, s::String)
rg = Base.Random.RangeGenerator(1:s.len)
while true
pos = rand(Base.Random.GLOBAL_RNG, rg)
isvalid_unsafe(s, pos) && return s[pos]
end
end

## rand from an iterator

rand_iter(rng, s, ::Base.IteratorSize) = throw(ArgumentError("iterator must have a known length"))

function rand_iter(rng::AbstractRNG, s, ::Union{Base.HasShape,Base.HasLength})::eltype(s)
pos = rand(rng, 1:length(s))
for (i, c) in enumerate(s)
i == pos && return c
end
end

## rand from a string for arrays
# we use collect(str), which is most of the time more efficient than specialized methods
# (except maybe for very small arrays)
rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str))
rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str)
rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = rand!(rng, Array{eltype(str)}(dims), str)
rand(rng::AbstractRNG, str::AbstractString, d1::Integer, dims::Integer...) = rand(rng, str, convert(Dims, tuple(d1, dims...)))
rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims)
rand(str::AbstractString, d1::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d1, dims...)

## random BitArrays (AbstractRNG)

function rand!(rng::AbstractRNG, B::BitArray)
Expand Down
13 changes: 9 additions & 4 deletions test/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()])
(Set(rand(Int, 20)), Int),
(Dict(zip(rand(Int,10), rand(Int, 10))), Pair{Int,Int}),
(1:100, Int),
(rand(Int, 100), Int)]
(rand(Int, 100), Int),
("qwèrtï", Char),
(Base.Test.GenericString("qwèrtï"), Char)]
b2 = big(2)
u3 = UInt(3)
for f in [rand, randn, randexp]
Expand All @@ -339,9 +341,12 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()])
a0 = rand(rng..., C) ::T
a1 = rand(rng..., C, 5) ::Vector{T}
a2 = rand(rng..., C, 2, 3) ::Array{T, 2}
a3 = rand!(rng..., Array{T}(5), C) ::Vector{T}
a4 = rand!(rng..., Array{T}(2, 3), C) ::Array{T, 2}
for a in [a0, a1..., a2..., a3..., a4...]
a3 = rand(rng..., C, (2, 3)) ::Array{T, 2}
a4 = rand!(rng..., Array{T}(5), C) ::Vector{T}
a5 = rand!(rng..., Array{T}(2, 3), C) ::Array{T, 2}
@test size(a1) == (5,)
@test size(a2) == size(a3) == (2, 3)
for a in [a0, a1..., a2..., a3..., a4..., a5...]
@test a in C
end
end
Expand Down

0 comments on commit 27d49ff

Please sign in to comment.