Skip to content

Commit

Permalink
Create RandomRetrievable protocols
Browse files Browse the repository at this point in the history
Types that conform to RandomRetrievable can have any random element be
taken from an instance. The `uncheckedRandom(using:)` method is unsafe.
It is a serious programming error to use it on an empty sequence.

The RandomRetrievableInRange protocol allows for getting random elements
from within a given index range. It is also a serious programming error
to use `uncheckedRandom(in:using:)` when the sequence or range is empty
or the range is out of bounds.

This removes `random(using:)` that could previously be used on any
Sequence. In theory, any Sequence can conform to RandomRetrievable and
RandomRetrievableInRange given Array's conformance to these protocols.
However, it would hide the horrible performance involved with allocating
memory for all elements of a sequence just to get a single random value
out. This is even worse when multiple random values are needed and thus
multiple arrays are allocated.

Types such as lazy sequence types will be able to conform to these
protocols once conditional conformance is implemented (SE-0143).
  • Loading branch information
nvzqz committed Mar 29, 2017
1 parent 77b674e commit 793e473
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 112 deletions.
20 changes: 10 additions & 10 deletions RandomKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
522268ED1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
522268EE1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
522268EF1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
5247B99D1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
5247B99E1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
5247B99F1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
5247B9A01E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
52485F671E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
52485F681E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
52485F691E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
Expand Down Expand Up @@ -149,10 +153,6 @@
52D47D3A1E313FCD0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
52D47D3B1E313FCE0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
52D47D3C1E313FCF0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
52D47D3D1E315D460050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
52D47D3E1E315D470050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
52D47D3F1E315D470050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
52D47D401E315D480050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
52D47D411E315D840050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
52D47D421E315D850050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
52D47D431E315D860050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
Expand Down Expand Up @@ -297,6 +297,7 @@
523D8B101DBEADD1006FF58C /* CGVector+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGVector+RandomKit.swift"; sourceTree = "<group>"; };
524083961CA5B31E00AB8851 /* RandomKit.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = RandomKit.playground; sourceTree = "<group>"; };
524083971CA5B35800AB8851 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomRetrievable.swift; sourceTree = "<group>"; };
52485F661E3FEE11007C53AC /* StackArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackArray.swift; sourceTree = "<group>"; };
525EB2BD1DBD607F00E8B246 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = RandomKit.xcodeproj/Info.plist; sourceTree = "<group>"; };
525EB2BF1DBD696B00E8B246 /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = "<group>"; };
Expand All @@ -314,7 +315,6 @@
52737DF31DBEBEED009B5E9D /* Dictionary+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+RandomKit.swift"; sourceTree = "<group>"; };
52737DF41DBEBEED009B5E9D /* FloatingPoint+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+RandomKit.swift"; sourceTree = "<group>"; };
52737DF51DBEBEED009B5E9D /* Integer+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Integer+RandomKit.swift"; sourceTree = "<group>"; };
52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sequence+RandomKit.swift"; sourceTree = "<group>"; };
52737DF81DBEBEED009B5E9D /* String+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+RandomKit.swift"; sourceTree = "<group>"; };
52737DF91DBEBEED009B5E9D /* UnicodeScalar+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+RandomKit.swift"; sourceTree = "<group>"; };
52737E281DBEBEF8009B5E9D /* Color+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+RandomKit.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -471,6 +471,7 @@
525EB33A1DBD829000E8B246 /* RandomWithinClosedRange.swift */,
52ADBE761DE54A98004CB28A /* RandomWithMaxWidth.swift */,
52ADBE7B1DE54BBF004CB28A /* RandomWithExactWidth.swift */,
5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */,
525EB2C01DBD696B00E8B246 /* Shuffleable.swift */,
5217714E1DE6D5EF00DD4584 /* UniqueShuffleable.swift */,
52FD11371DE2811C002EF43E /* RandomRawRepresentable.swift */,
Expand Down Expand Up @@ -501,7 +502,6 @@
52737DF41DBEBEED009B5E9D /* FloatingPoint+RandomKit.swift */,
52737DF51DBEBEED009B5E9D /* Integer+RandomKit.swift */,
5230293B1DC54137007530CD /* Range+RandomKit.swift */,
52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */,
52A6B1311E329045001533D3 /* Set+RandomKit.swift */,
52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */,
52737DF81DBEBEED009B5E9D /* String+RandomKit.swift */,
Expand Down Expand Up @@ -953,6 +953,7 @@
524F1E561E31364B00BDE35F /* Array+RandomKit.swift in Sources */,
524F1E0D1E312E2900BDE35F /* RandomToValue.swift in Sources */,
524F1E521E31360300BDE35F /* Character+RandomKit.swift in Sources */,
5247B99D1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
52CA181F1E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
52D47D411E315D840050588E /* Strideable+RandomKit.swift in Sources */,
524F1E051E312E0500BDE35F /* RandomWithMax.swift in Sources */,
Expand All @@ -961,7 +962,6 @@
524F1E391E31312800BDE35F /* Integer+RandomKit.swift in Sources */,
524F1E211E312F8100BDE35F /* RandomWithExactWidth.swift in Sources */,
524F1E1D1E312F6000BDE35F /* RandomWithMaxWidth.swift in Sources */,
52D47D3D1E315D460050588E /* Sequence+RandomKit.swift in Sources */,
52485F671E3FEE11007C53AC /* StackArray.swift in Sources */,
524F1E251E312FCE00BDE35F /* UniqueShuffleable.swift in Sources */,
5290FC561E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
Expand Down Expand Up @@ -1012,6 +1012,7 @@
524F1E571E31364C00BDE35F /* Array+RandomKit.swift in Sources */,
524F1E0E1E312E2900BDE35F /* RandomToValue.swift in Sources */,
524F1E531E31360400BDE35F /* Character+RandomKit.swift in Sources */,
5247B99E1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
52CA18201E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
52D47D421E315D850050588E /* Strideable+RandomKit.swift in Sources */,
524F1E061E312E0500BDE35F /* RandomWithMax.swift in Sources */,
Expand All @@ -1020,7 +1021,6 @@
524F1E3A1E31312900BDE35F /* Integer+RandomKit.swift in Sources */,
524F1E221E312F8100BDE35F /* RandomWithExactWidth.swift in Sources */,
524F1E1E1E312F6000BDE35F /* RandomWithMaxWidth.swift in Sources */,
52D47D3E1E315D470050588E /* Sequence+RandomKit.swift in Sources */,
52485F681E3FEE11007C53AC /* StackArray.swift in Sources */,
524F1E261E312FCF00BDE35F /* UniqueShuffleable.swift in Sources */,
5290FC571E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
Expand Down Expand Up @@ -1071,6 +1071,7 @@
524F1E581E31364C00BDE35F /* Array+RandomKit.swift in Sources */,
524F1E0F1E312E2A00BDE35F /* RandomToValue.swift in Sources */,
524F1E541E31360500BDE35F /* Character+RandomKit.swift in Sources */,
5247B99F1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
52CA18211E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
52D47D431E315D860050588E /* Strideable+RandomKit.swift in Sources */,
524F1E071E312E0600BDE35F /* RandomWithMax.swift in Sources */,
Expand All @@ -1079,7 +1080,6 @@
524F1E3B1E31312900BDE35F /* Integer+RandomKit.swift in Sources */,
524F1E231E312F8200BDE35F /* RandomWithExactWidth.swift in Sources */,
524F1E1F1E312F6100BDE35F /* RandomWithMaxWidth.swift in Sources */,
52D47D3F1E315D470050588E /* Sequence+RandomKit.swift in Sources */,
52485F691E3FEE11007C53AC /* StackArray.swift in Sources */,
524F1E271E312FCF00BDE35F /* UniqueShuffleable.swift in Sources */,
5290FC581E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
Expand Down Expand Up @@ -1154,6 +1154,7 @@
524F1E591E31364D00BDE35F /* Array+RandomKit.swift in Sources */,
524F1E101E312E2A00BDE35F /* RandomToValue.swift in Sources */,
524F1E551E31360600BDE35F /* Character+RandomKit.swift in Sources */,
5247B9A01E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
52CA18221E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
52D47D441E315D870050588E /* Strideable+RandomKit.swift in Sources */,
524F1E081E312E0600BDE35F /* RandomWithMax.swift in Sources */,
Expand All @@ -1162,7 +1163,6 @@
524F1E3C1E31312A00BDE35F /* Integer+RandomKit.swift in Sources */,
524F1E241E312F8200BDE35F /* RandomWithExactWidth.swift in Sources */,
524F1E201E312F6100BDE35F /* RandomWithMaxWidth.swift in Sources */,
52D47D401E315D480050588E /* Sequence+RandomKit.swift in Sources */,
52485F6A1E3FEE11007C53AC /* StackArray.swift in Sources */,
524F1E281E312FD000BDE35F /* UniqueShuffleable.swift in Sources */,
5290FC591E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
Expand Down
29 changes: 23 additions & 6 deletions Sources/RandomKit/Extensions/Foundation/NSArray+RandomKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,32 @@

import Foundation

extension NSArray {
/// Returns a random element of `self`, or `nil` if `self` is empty.
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Any? {
let count = self.count
guard count > 0 else {
extension NSArray: RandomRetrievableInRange {

/// Returns a random element in `range` without checking whether `self` or `range` is empty.
public func uncheckedRandom<R: RandomGenerator>(in range: Range<Int>, using randomGenerator: inout R) -> Any {
let index = Int.uncheckedRandom(within: range, using: &randomGenerator)
return self[index]
}

/// Returns a random element in `self` without checking whether `self` is empty.
public func uncheckedRandom<R: RandomGenerator>(using randomGenerator: inout R) -> Any {
return uncheckedRandom(in: Range(uncheckedBounds: (0, count)), using: &randomGenerator)
}

/// Returns an optional random element in `range`. The result is `nil` if `self` or `range` is empty.
public func random<R: RandomGenerator>(in range: Range<Int>, using randomGenerator: inout R) -> Any? {
guard count > 0 && range.upperBound > range.lowerBound else {
return nil
}
return self[Int.random(to: count, using: &randomGenerator)]
return uncheckedRandom(in: range, using: &randomGenerator)
}

/// Returns an optional random element in `self`. The result is `nil` if `self` is empty.
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Any? {
return count > 0 ? uncheckedRandom(using: &randomGenerator) : nil
}

}

extension NSMutableArray: ShuffleableInRange, UniqueShuffleableInRange {
Expand Down
26 changes: 0 additions & 26 deletions Sources/RandomKit/Extensions/Swift/Array+RandomKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,32 +142,6 @@ extension Array where Element: RandomWithinClosedRange {
}
}

#if !swift(>=3.1)
extension ContiguousArray {
/// Returns a random element of `self`, or `nil` if `self` is empty.
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Element? {
guard let index = _indexRange.random(using: &randomGenerator) else {
return nil
}
return _buffer.firstElementAddress[index]
}
}

extension Array {
/// Returns a random element of `self`, or `nil` if `self` is empty.
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Element? {
guard let index = _indexRange.random(using: &randomGenerator) else {
return nil
}
if let address = _buffer.firstElementAddressIfContiguous {
return address[index]
} else {
return self[index]
}
}
}
#endif

extension Array {

/// Returns an array of randomly choosen elements.
Expand Down
Loading

0 comments on commit 793e473

Please sign in to comment.