Skip to content

Commit 793e473

Browse files
committed
Create RandomRetrievable protocols
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).
1 parent 77b674e commit 793e473

File tree

11 files changed

+251
-112
lines changed

11 files changed

+251
-112
lines changed

RandomKit.xcodeproj/project.pbxproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
522268ED1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
1212
522268EE1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
1313
522268EF1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
14+
5247B99D1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
15+
5247B99E1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
16+
5247B99F1E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
17+
5247B9A01E890BB200C6B60E /* RandomRetrievable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */; };
1418
52485F671E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
1519
52485F681E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
1620
52485F691E3FEE11007C53AC /* StackArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52485F661E3FEE11007C53AC /* StackArray.swift */; };
@@ -149,10 +153,6 @@
149153
52D47D3A1E313FCD0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
150154
52D47D3B1E313FCE0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
151155
52D47D3C1E313FCF0050588E /* Range+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5230293B1DC54137007530CD /* Range+RandomKit.swift */; };
152-
52D47D3D1E315D460050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
153-
52D47D3E1E315D470050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
154-
52D47D3F1E315D470050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
155-
52D47D401E315D480050588E /* Sequence+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */; };
156156
52D47D411E315D840050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
157157
52D47D421E315D850050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
158158
52D47D431E315D860050588E /* Strideable+RandomKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */; };
@@ -297,6 +297,7 @@
297297
523D8B101DBEADD1006FF58C /* CGVector+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGVector+RandomKit.swift"; sourceTree = "<group>"; };
298298
524083961CA5B31E00AB8851 /* RandomKit.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = RandomKit.playground; sourceTree = "<group>"; };
299299
524083971CA5B35800AB8851 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
300+
5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomRetrievable.swift; sourceTree = "<group>"; };
300301
52485F661E3FEE11007C53AC /* StackArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackArray.swift; sourceTree = "<group>"; };
301302
525EB2BD1DBD607F00E8B246 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = RandomKit.xcodeproj/Info.plist; sourceTree = "<group>"; };
302303
525EB2BF1DBD696B00E8B246 /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = "<group>"; };
@@ -314,7 +315,6 @@
314315
52737DF31DBEBEED009B5E9D /* Dictionary+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+RandomKit.swift"; sourceTree = "<group>"; };
315316
52737DF41DBEBEED009B5E9D /* FloatingPoint+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+RandomKit.swift"; sourceTree = "<group>"; };
316317
52737DF51DBEBEED009B5E9D /* Integer+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Integer+RandomKit.swift"; sourceTree = "<group>"; };
317-
52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sequence+RandomKit.swift"; sourceTree = "<group>"; };
318318
52737DF81DBEBEED009B5E9D /* String+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+RandomKit.swift"; sourceTree = "<group>"; };
319319
52737DF91DBEBEED009B5E9D /* UnicodeScalar+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+RandomKit.swift"; sourceTree = "<group>"; };
320320
52737E281DBEBEF8009B5E9D /* Color+RandomKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+RandomKit.swift"; sourceTree = "<group>"; };
@@ -471,6 +471,7 @@
471471
525EB33A1DBD829000E8B246 /* RandomWithinClosedRange.swift */,
472472
52ADBE761DE54A98004CB28A /* RandomWithMaxWidth.swift */,
473473
52ADBE7B1DE54BBF004CB28A /* RandomWithExactWidth.swift */,
474+
5247B99C1E890BB200C6B60E /* RandomRetrievable.swift */,
474475
525EB2C01DBD696B00E8B246 /* Shuffleable.swift */,
475476
5217714E1DE6D5EF00DD4584 /* UniqueShuffleable.swift */,
476477
52FD11371DE2811C002EF43E /* RandomRawRepresentable.swift */,
@@ -501,7 +502,6 @@
501502
52737DF41DBEBEED009B5E9D /* FloatingPoint+RandomKit.swift */,
502503
52737DF51DBEBEED009B5E9D /* Integer+RandomKit.swift */,
503504
5230293B1DC54137007530CD /* Range+RandomKit.swift */,
504-
52737DF61DBEBEED009B5E9D /* Sequence+RandomKit.swift */,
505505
52A6B1311E329045001533D3 /* Set+RandomKit.swift */,
506506
52A248B41DC68200004C9991 /* Strideable+RandomKit.swift */,
507507
52737DF81DBEBEED009B5E9D /* String+RandomKit.swift */,
@@ -953,6 +953,7 @@
953953
524F1E561E31364B00BDE35F /* Array+RandomKit.swift in Sources */,
954954
524F1E0D1E312E2900BDE35F /* RandomToValue.swift in Sources */,
955955
524F1E521E31360300BDE35F /* Character+RandomKit.swift in Sources */,
956+
5247B99D1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
956957
52CA181F1E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
957958
52D47D411E315D840050588E /* Strideable+RandomKit.swift in Sources */,
958959
524F1E051E312E0500BDE35F /* RandomWithMax.swift in Sources */,
@@ -961,7 +962,6 @@
961962
524F1E391E31312800BDE35F /* Integer+RandomKit.swift in Sources */,
962963
524F1E211E312F8100BDE35F /* RandomWithExactWidth.swift in Sources */,
963964
524F1E1D1E312F6000BDE35F /* RandomWithMaxWidth.swift in Sources */,
964-
52D47D3D1E315D460050588E /* Sequence+RandomKit.swift in Sources */,
965965
52485F671E3FEE11007C53AC /* StackArray.swift in Sources */,
966966
524F1E251E312FCE00BDE35F /* UniqueShuffleable.swift in Sources */,
967967
5290FC561E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
@@ -1012,6 +1012,7 @@
10121012
524F1E571E31364C00BDE35F /* Array+RandomKit.swift in Sources */,
10131013
524F1E0E1E312E2900BDE35F /* RandomToValue.swift in Sources */,
10141014
524F1E531E31360400BDE35F /* Character+RandomKit.swift in Sources */,
1015+
5247B99E1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
10151016
52CA18201E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
10161017
52D47D421E315D850050588E /* Strideable+RandomKit.swift in Sources */,
10171018
524F1E061E312E0500BDE35F /* RandomWithMax.swift in Sources */,
@@ -1020,7 +1021,6 @@
10201021
524F1E3A1E31312900BDE35F /* Integer+RandomKit.swift in Sources */,
10211022
524F1E221E312F8100BDE35F /* RandomWithExactWidth.swift in Sources */,
10221023
524F1E1E1E312F6000BDE35F /* RandomWithMaxWidth.swift in Sources */,
1023-
52D47D3E1E315D470050588E /* Sequence+RandomKit.swift in Sources */,
10241024
52485F681E3FEE11007C53AC /* StackArray.swift in Sources */,
10251025
524F1E261E312FCF00BDE35F /* UniqueShuffleable.swift in Sources */,
10261026
5290FC571E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
@@ -1071,6 +1071,7 @@
10711071
524F1E581E31364C00BDE35F /* Array+RandomKit.swift in Sources */,
10721072
524F1E0F1E312E2A00BDE35F /* RandomToValue.swift in Sources */,
10731073
524F1E541E31360500BDE35F /* Character+RandomKit.swift in Sources */,
1074+
5247B99F1E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
10741075
52CA18211E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
10751076
52D47D431E315D860050588E /* Strideable+RandomKit.swift in Sources */,
10761077
524F1E071E312E0600BDE35F /* RandomWithMax.swift in Sources */,
@@ -1079,7 +1080,6 @@
10791080
524F1E3B1E31312900BDE35F /* Integer+RandomKit.swift in Sources */,
10801081
524F1E231E312F8200BDE35F /* RandomWithExactWidth.swift in Sources */,
10811082
524F1E1F1E312F6100BDE35F /* RandomWithMaxWidth.swift in Sources */,
1082-
52D47D3F1E315D470050588E /* Sequence+RandomKit.swift in Sources */,
10831083
52485F691E3FEE11007C53AC /* StackArray.swift in Sources */,
10841084
524F1E271E312FCF00BDE35F /* UniqueShuffleable.swift in Sources */,
10851085
5290FC581E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,
@@ -1154,6 +1154,7 @@
11541154
524F1E591E31364D00BDE35F /* Array+RandomKit.swift in Sources */,
11551155
524F1E101E312E2A00BDE35F /* RandomToValue.swift in Sources */,
11561156
524F1E551E31360600BDE35F /* Character+RandomKit.swift in Sources */,
1157+
5247B9A01E890BB200C6B60E /* RandomRetrievable.swift in Sources */,
11571158
52CA18221E63A818004C18FA /* NSArray+RandomKit.swift in Sources */,
11581159
52D47D441E315D870050588E /* Strideable+RandomKit.swift in Sources */,
11591160
524F1E081E312E0600BDE35F /* RandomWithMax.swift in Sources */,
@@ -1162,7 +1163,6 @@
11621163
524F1E3C1E31312A00BDE35F /* Integer+RandomKit.swift in Sources */,
11631164
524F1E241E312F8200BDE35F /* RandomWithExactWidth.swift in Sources */,
11641165
524F1E201E312F6100BDE35F /* RandomWithMaxWidth.swift in Sources */,
1165-
52D47D401E315D480050588E /* Sequence+RandomKit.swift in Sources */,
11661166
52485F6A1E3FEE11007C53AC /* StackArray.swift in Sources */,
11671167
524F1E281E312FD000BDE35F /* UniqueShuffleable.swift in Sources */,
11681168
5290FC591E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift in Sources */,

Sources/RandomKit/Extensions/Foundation/NSArray+RandomKit.swift

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,32 @@
2727

2828
import Foundation
2929

30-
extension NSArray {
31-
/// Returns a random element of `self`, or `nil` if `self` is empty.
32-
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Any? {
33-
let count = self.count
34-
guard count > 0 else {
30+
extension NSArray: RandomRetrievableInRange {
31+
32+
/// Returns a random element in `range` without checking whether `self` or `range` is empty.
33+
public func uncheckedRandom<R: RandomGenerator>(in range: Range<Int>, using randomGenerator: inout R) -> Any {
34+
let index = Int.uncheckedRandom(within: range, using: &randomGenerator)
35+
return self[index]
36+
}
37+
38+
/// Returns a random element in `self` without checking whether `self` is empty.
39+
public func uncheckedRandom<R: RandomGenerator>(using randomGenerator: inout R) -> Any {
40+
return uncheckedRandom(in: Range(uncheckedBounds: (0, count)), using: &randomGenerator)
41+
}
42+
43+
/// Returns an optional random element in `range`. The result is `nil` if `self` or `range` is empty.
44+
public func random<R: RandomGenerator>(in range: Range<Int>, using randomGenerator: inout R) -> Any? {
45+
guard count > 0 && range.upperBound > range.lowerBound else {
3546
return nil
3647
}
37-
return self[Int.random(to: count, using: &randomGenerator)]
48+
return uncheckedRandom(in: range, using: &randomGenerator)
3849
}
50+
51+
/// Returns an optional random element in `self`. The result is `nil` if `self` is empty.
52+
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Any? {
53+
return count > 0 ? uncheckedRandom(using: &randomGenerator) : nil
54+
}
55+
3956
}
4057

4158
extension NSMutableArray: ShuffleableInRange, UniqueShuffleableInRange {

Sources/RandomKit/Extensions/Swift/Array+RandomKit.swift

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -142,32 +142,6 @@ extension Array where Element: RandomWithinClosedRange {
142142
}
143143
}
144144

145-
#if !swift(>=3.1)
146-
extension ContiguousArray {
147-
/// Returns a random element of `self`, or `nil` if `self` is empty.
148-
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Element? {
149-
guard let index = _indexRange.random(using: &randomGenerator) else {
150-
return nil
151-
}
152-
return _buffer.firstElementAddress[index]
153-
}
154-
}
155-
156-
extension Array {
157-
/// Returns a random element of `self`, or `nil` if `self` is empty.
158-
public func random<R: RandomGenerator>(using randomGenerator: inout R) -> Element? {
159-
guard let index = _indexRange.random(using: &randomGenerator) else {
160-
return nil
161-
}
162-
if let address = _buffer.firstElementAddressIfContiguous {
163-
return address[index]
164-
} else {
165-
return self[index]
166-
}
167-
}
168-
}
169-
#endif
170-
171145
extension Array {
172146

173147
/// Returns an array of randomly choosen elements.

0 commit comments

Comments
 (0)