Skip to content

Commit c833d85

Browse files
committed
stdlib: Remove Collection.randomElement customization point.
As proposed and accepted in amendment swiftlang/swift-evolution#863. rdar://problem/41067949
1 parent d7ff0a3 commit c833d85

File tree

2 files changed

+30
-112
lines changed

2 files changed

+30
-112
lines changed

stdlib/public/core/Collection.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -803,25 +803,6 @@ public protocol Collection: Sequence where SubSequence: Collection {
803803
/// `endIndex`.
804804
func formIndex(after i: inout Index)
805805

806-
/// Returns a random element of the collection, using the given generator as
807-
/// a source for randomness.
808-
///
809-
/// You use this method to select a random element from a collection when you
810-
/// are using a custom random number generator. For example, call
811-
/// `randomElement(using:)` to select a random element from an array of names.
812-
///
813-
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
814-
/// let randomName = names.randomElement(using: &myGenerator)!
815-
/// // randomName == "Amani"
816-
///
817-
/// - Parameter generator: The random number generator to use when choosing
818-
/// a random element.
819-
/// - Returns: A random element from the collection. If the collection is
820-
/// empty, the method returns `nil`.
821-
func randomElement<T: RandomNumberGenerator>(
822-
using generator: inout T
823-
) -> Element?
824-
825806
@available(*, deprecated, message: "all index distances are now of type Int")
826807
typealias IndexDistance = Int
827808
}

stdlib/public/core/Integers.swift.gyb

Lines changed: 30 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,98 +2540,6 @@ ${assignmentOperatorComment(x.operator, False)}
25402540
% for Range in ['Range', 'ClosedRange']:
25412541
% exampleRange = '1..<100' if Range == 'Range' else '1...100'
25422542
2543-
extension ${Range}
2544-
where Bound: FixedWidthInteger, Bound.Stride : SignedInteger,
2545-
Bound.Magnitude: UnsignedInteger
2546-
{
2547-
2548-
/// Returns a random element of the range, using the given generator as
2549-
/// a source for randomness.
2550-
///
2551-
/// You can use this method to select a random element of a range when you
2552-
/// are using a custom random number generator. If you're generating a random
2553-
/// number, in most cases, you should prefer using the `random(in:using:)`
2554-
/// static method of the desired numeric type. That static method is available
2555-
/// for both integer and floating point types, and returns a non-optional
2556-
/// value.
2557-
///
2558-
/// - Parameter generator: The random number generator to use when choosing
2559-
/// a random element.
2560-
/// - Returns: A random element of the range.
2561-
% if 'Closed' not in Range:
2562-
/// If the range is empty, the method returns `nil`.
2563-
% else:
2564-
/// This method never returns `nil`.
2565-
% end
2566-
@inlinable
2567-
public func randomElement<T: RandomNumberGenerator>(
2568-
using generator: inout T
2569-
) -> Element? {
2570-
guard !isEmpty else {
2571-
return nil
2572-
}
2573-
let isLowerNegative = Bound.isSigned && lowerBound < 0
2574-
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
2575-
% if 'Closed' not in Range:
2576-
let delta: Bound.Magnitude
2577-
% else:
2578-
var delta: Bound.Magnitude
2579-
% end
2580-
if isLowerNegative {
2581-
delta = sameSign
2582-
? lowerBound.magnitude - upperBound.magnitude
2583-
: lowerBound.magnitude + upperBound.magnitude
2584-
} else {
2585-
delta = upperBound.magnitude - lowerBound.magnitude
2586-
}
2587-
% if 'Closed' in Range:
2588-
if delta == Bound.Magnitude.max {
2589-
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
2590-
}
2591-
delta += 1
2592-
% end
2593-
let randomMagnitude = generator.next(upperBound: delta)
2594-
if sameSign {
2595-
return lowerBound + Bound(randomMagnitude)
2596-
} else {
2597-
% if 'Closed' not in Range:
2598-
return randomMagnitude < upperBound.magnitude
2599-
? Bound(randomMagnitude)
2600-
: -1 - Bound(randomMagnitude - upperBound.magnitude)
2601-
% else:
2602-
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
2603-
? Bound(randomMagnitude)
2604-
: 0 - Bound(randomMagnitude - upperBound.magnitude)
2605-
% end
2606-
}
2607-
}
2608-
2609-
/// Returns a random element of the range, using the given generator as
2610-
/// a source for randomness.
2611-
///
2612-
/// You can use this method to select a random element of a range when you
2613-
/// are using a custom random number generator. If you're generating a random
2614-
/// number, in most cases, you should prefer using the `random(in:)`
2615-
/// static method of the desired numeric type. That static method is available
2616-
/// for both integer and floating point types, and returns a non-optional
2617-
/// value.
2618-
///
2619-
/// This method uses the default random generator, `Random.default`. Calling
2620-
/// `(${exampleRange}).randomElement()` is equivalent to calling
2621-
/// `(${exampleRange}).randomElement(using: &Random.default)`.
2622-
///
2623-
/// - Returns: A random element of the range.
2624-
% if 'Closed' not in Range:
2625-
/// If the range is empty, the method returns `nil`.
2626-
% else:
2627-
/// This method never returns `nil`.
2628-
% end
2629-
@inlinable
2630-
public func randomElement() -> Element? {
2631-
return randomElement(using: &Random.default)
2632-
}
2633-
}
2634-
26352543
extension FixedWidthInteger
26362544
where Self.Stride : SignedInteger,
26372545
Self.Magnitude : UnsignedInteger {
@@ -2667,7 +2575,36 @@ where Self.Stride : SignedInteger,
26672575
!range.isEmpty,
26682576
"Can't get random value with an empty range"
26692577
)
2670-
return range.randomElement(using: &generator)!
2578+
2579+
// Compute delta, the distance between the lower and upper bounds. This
2580+
// value may not representable by the type Bound if Bound is signed, but
2581+
// is always representable as Bound.Magnitude.
2582+
% if 'Closed' in Range:
2583+
var delta = Magnitude(truncatingIfNeeded: range.upperBound &- range.lowerBound)
2584+
% else:
2585+
let delta = Magnitude(truncatingIfNeeded: range.upperBound &- range.lowerBound)
2586+
% end
2587+
% if 'Closed' in Range:
2588+
// Subtle edge case: if the range is the whole set of representable values,
2589+
// then adding one to delta to account for a closed range will overflow.
2590+
// If we used &+ instead, the result would be zero, which isn't helpful,
2591+
// so we actually need to handle this case separately.
2592+
if delta == Magnitude.max {
2593+
return Self(truncatingIfNeeded: generator.next() as Magnitude)
2594+
}
2595+
// Need to widen delta to account for the right-endpoint of a closed range.
2596+
delta += 1
2597+
% end
2598+
// The mathematical result we want is lowerBound plus a random value in
2599+
// 0 ..< delta. We need to be slightly careful about how we do this
2600+
// arithmetic; the Bound type cannot generally represent the random value,
2601+
// so we use a wrapping addition on Bound.Magnitude. This will often
2602+
// overflow, but produces the correct bit pattern for the result when
2603+
// converted back to Bound.
2604+
return Self(truncatingIfNeeded:
2605+
Magnitude(truncatingIfNeeded: range.lowerBound) &+
2606+
generator.next(upperBound: delta)
2607+
)
26712608
}
26722609
26732610
/// Returns a random value within the specified range.

0 commit comments

Comments
 (0)