Skip to content

[4.2] stdlib: Remove Collection.randomElement customization point. #17639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions stdlib/public/core/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -803,25 +803,6 @@ public protocol Collection: Sequence where SubSequence: Collection {
/// `endIndex`.
func formIndex(after i: inout Index)

/// Returns a random element of the collection, using the given generator as
/// a source for randomness.
///
/// You use this method to select a random element from a collection when you
/// are using a custom random number generator. For example, call
/// `randomElement(using:)` to select a random element from an array of names.
///
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
/// let randomName = names.randomElement(using: &myGenerator)!
/// // randomName == "Amani"
///
/// - Parameter generator: The random number generator to use when choosing
/// a random element.
/// - Returns: A random element from the collection. If the collection is
/// empty, the method returns `nil`.
func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element?

@available(*, deprecated, message: "all index distances are now of type Int")
typealias IndexDistance = Int
}
Expand Down
123 changes: 30 additions & 93 deletions stdlib/public/core/Integers.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2540,98 +2540,6 @@ ${assignmentOperatorComment(x.operator, False)}
% for Range in ['Range', 'ClosedRange']:
% exampleRange = '1..<100' if Range == 'Range' else '1...100'

extension ${Range}
where Bound: FixedWidthInteger, Bound.Stride : SignedInteger,
Bound.Magnitude: UnsignedInteger
{

/// Returns a random element of the range, using the given generator as
/// a source for randomness.
///
/// You can use this method to select a random element of a range when you
/// are using a custom random number generator. If you're generating a random
/// number, in most cases, you should prefer using the `random(in:using:)`
/// static method of the desired numeric type. That static method is available
/// for both integer and floating point types, and returns a non-optional
/// value.
///
/// - Parameter generator: The random number generator to use when choosing
/// a random element.
/// - Returns: A random element of the range.
% if 'Closed' not in Range:
/// If the range is empty, the method returns `nil`.
% else:
/// This method never returns `nil`.
% end
@inlinable
public func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element? {
guard !isEmpty else {
return nil
}
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
% if 'Closed' not in Range:
let delta: Bound.Magnitude
% else:
var delta: Bound.Magnitude
% end
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
% if 'Closed' in Range:
if delta == Bound.Magnitude.max {
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
}
delta += 1
% end
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
% if 'Closed' not in Range:
return randomMagnitude < upperBound.magnitude
? Bound(randomMagnitude)
: -1 - Bound(randomMagnitude - upperBound.magnitude)
% else:
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
? Bound(randomMagnitude)
: 0 - Bound(randomMagnitude - upperBound.magnitude)
% end
}
}

/// Returns a random element of the range, using the given generator as
/// a source for randomness.
///
/// You can use this method to select a random element of a range when you
/// are using a custom random number generator. If you're generating a random
/// number, in most cases, you should prefer using the `random(in:)`
/// static method of the desired numeric type. That static method is available
/// for both integer and floating point types, and returns a non-optional
/// value.
///
/// This method uses the default random generator, `Random.default`. Calling
/// `(${exampleRange}).randomElement()` is equivalent to calling
/// `(${exampleRange}).randomElement(using: &Random.default)`.
///
/// - Returns: A random element of the range.
% if 'Closed' not in Range:
/// If the range is empty, the method returns `nil`.
% else:
/// This method never returns `nil`.
% end
@inlinable
public func randomElement() -> Element? {
return randomElement(using: &Random.default)
}
}

extension FixedWidthInteger
where Self.Stride : SignedInteger,
Self.Magnitude : UnsignedInteger {
Expand Down Expand Up @@ -2667,7 +2575,36 @@ where Self.Stride : SignedInteger,
!range.isEmpty,
"Can't get random value with an empty range"
)
return range.randomElement(using: &generator)!

// Compute delta, the distance between the lower and upper bounds. This
// value may not representable by the type Bound if Bound is signed, but
// is always representable as Bound.Magnitude.
% if 'Closed' in Range:
var delta = Magnitude(truncatingIfNeeded: range.upperBound &- range.lowerBound)
% else:
let delta = Magnitude(truncatingIfNeeded: range.upperBound &- range.lowerBound)
% end
% if 'Closed' in Range:
// Subtle edge case: if the range is the whole set of representable values,
// then adding one to delta to account for a closed range will overflow.
// If we used &+ instead, the result would be zero, which isn't helpful,
// so we actually need to handle this case separately.
if delta == Magnitude.max {
return Self(truncatingIfNeeded: generator.next() as Magnitude)
}
// Need to widen delta to account for the right-endpoint of a closed range.
delta += 1
% end
// The mathematical result we want is lowerBound plus a random value in
// 0 ..< delta. We need to be slightly careful about how we do this
// arithmetic; the Bound type cannot generally represent the random value,
// so we use a wrapping addition on Bound.Magnitude. This will often
// overflow, but produces the correct bit pattern for the result when
// converted back to Bound.
return Self(truncatingIfNeeded:
Magnitude(truncatingIfNeeded: range.lowerBound) &+
generator.next(upperBound: delta)
)
}

/// Returns a random value within the specified range.
Expand Down