@@ -1565,19 +1565,27 @@ module Enumerable(T)
15651565 reject { |e | pattern === e }
15661566 end
15671567
1568- # Returns an `Array` of *n* random elements from `self`, using the given
1569- # *random* number generator. All elements have equal probability of being
1570- # drawn. Sampling is done without replacement; if *n* is larger than the size
1571- # of this collection, the returned `Array` has the same size as `self`.
1568+ # Returns an `Array` of *n* random elements from `self`. All elements have
1569+ # equal probability of being drawn. Sampling is done without replacement; if
1570+ # *n* is larger than the size of this collection, the returned `Array` has the
1571+ # same size as `self`.
15721572 #
15731573 # Raises `ArgumentError` if *n* is negative.
15741574 #
15751575 # ```
1576- # [1, 2, 3, 4, 5].sample(2) # => [3, 5]
1577- # {1, 2, 3, 4, 5}.sample(2) # => [3, 4]
1578- # {1, 2, 3, 4, 5}.sample(2, Random.new(1)) # => [1, 5]
1576+ # [1, 2, 3, 4, 5].sample(2) # => [3, 5]
1577+ # {1, 2, 3, 4, 5}.sample(2) # => [3, 4]
15791578 # ```
1580- def sample (n : Int , random : Random = Random ::DEFAULT ) : Array (T )
1579+ #
1580+ # Uses the *random* instance when provided if the randomness needs to be
1581+ # controlled or to follow some traits. For example the following calls use a
1582+ # custom seed or a secure random source:
1583+ #
1584+ # ```
1585+ # {1, 2, 3, 4, 5}.sample(2, Random.new(1)) # => [1, 5]
1586+ # {1, 2, 3, 4, 5}.sample(2, Random::Secure) # => [2, 5]
1587+ # ```
1588+ def sample (n : Int , random : Random ? = nil ) : Array (T )
15811589 raise ArgumentError .new(" Can't sample negative number of elements" ) if n < 0
15821590
15831591 # Unweighted reservoir sampling:
@@ -1588,40 +1596,54 @@ module Enumerable(T)
15881596 ary = Array (T ).new(n)
15891597 return ary if n == 0
15901598
1599+ # FIXME: thread unsafe (#each may yield and the fiber switch threads)
1600+ rng = random || Random ::DEFAULT
1601+
15911602 each_with_index do |elem , i |
15921603 if i < n
15931604 ary << elem
15941605 else
1595- j = random .rand(i + 1 )
1606+ j = rng .rand(i + 1 )
15961607 if j < n
15971608 ary.to_unsafe[j] = elem
15981609 end
15991610 end
16001611 end
16011612
1602- ary.shuffle!(random )
1613+ ary.shuffle!(rng )
16031614 end
16041615
1605- # Returns a random element from `self`, using the given *random* number
1606- # generator. All elements have equal probability of being drawn.
1616+ # Returns a random element from `self`. All elements have equal probability of
1617+ # being drawn.
16071618 #
16081619 # Raises `IndexError` if `self` is empty.
16091620 #
16101621 # ```
16111622 # a = [1, 2, 3]
1612- # a.sample # => 2
1613- # a.sample # => 1
1614- # a.sample(Random.new(1)) # => 3
1623+ # a.sample # => 2
1624+ # a.sample # => 1
16151625 # ```
1616- def sample (random : Random = Random ::DEFAULT ) : T
1626+ #
1627+ # Uses the *random* instance when provided if the randomness needs to be
1628+ # controlled or to follow some traits. For example the following calls use a
1629+ # custom seed or a secure random source:
1630+ #
1631+ # ```
1632+ # a.sample(Random.new(1)) # => 3
1633+ # a.sample(Random::Secure) # => 1
1634+ # ```
1635+ def sample (random : Random ? = nil ) : T
16171636 value = uninitialized T
16181637 found = false
16191638
1639+ # FIXME: thread unsafe (#each may yield and the fiber switch threads)
1640+ rng = random || Random ::DEFAULT
1641+
16201642 each_with_index do |elem , i |
16211643 if ! found
16221644 value = elem
16231645 found = true
1624- elsif random .rand(i + 1 ) == 0
1646+ elsif rng .rand(i + 1 ) == 0
16251647 value = elem
16261648 end
16271649 end
0 commit comments