diff --git a/base/random.jl b/base/random.jl index dd49d7424fbe7..4a047f01ce673 100644 --- a/base/random.jl +++ b/base/random.jl @@ -264,10 +264,6 @@ Pick a random element or array of random elements from the set of values specifi `S` defaults to [`Float64`](@ref). -Note that the complexity of `rand(rng, s::AbstractString)` is linear -in the length of `s` if `s` is not of type `String`. If called more -than a few times, you should use `rand(rng, collect(s))` instead. - # Examples ```julia-repl @@ -691,32 +687,27 @@ 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 = RangeGenerator(1:s.len) +function rand(rng::AbstractRNG, s::String)::Char + g = RangeGenerator(1:s.len) while true - pos = rand(rng, rg) + pos = rand(rng, g) 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 +function rand(rng::AbstractRNG, s::AbstractString)::Char + g = RangeGenerator(1:endof(s)) + while true + try # the generic `isvalid` includes an equivalent try/catch statement + return s[rand(rng, g)] + end end end +rand(s::AbstractString) = rand(GLOBAL_RNG, s) + ## 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)