I use this extension on MutableCollection
extension MutableCollectionType where Index == Int {
mutating func shuffleInPlace() {
let c = count + startIndex
if count < 2 { return }
for i in startIndex..<c - 1 {
let j = Int(arc4random_uniform(UInt32(c - i))) + i
guard i != j else { continue }
swap(&self[i], &self[j])
}
}
}
It works fine with Array, but not with ArraySlice - both confirm MutableCollection protocol
// Array
var numbers3 = [1, 2, 3, 4, 5, 6, 7]
numbers3.shuffleInPlace() // OutPut: [6, 4, 3, 1, 5, 2, 7]
// ArraySlice PROBLEM HERE: Wrong Result
var numbers3Slice: ArraySlice<Int> = numbers3[1..<4].shuffleInPlace()
print (numbers3Slice) // OutPut:"()\n"
// Though
var rangeA = numbers3[1..<5] /
rangeA.shuffleInPlace() // OutPut: [1, 3, 4, 5]
No erros from compiler. I know numbers[1..<4] is immutable, but compiler doesn't see it.
In case of Array
[1, 2, 3, 4, 5, 6, 7].shuffleInPlace()
Compiler give me an error :cannot use mutating member on immutable value of type '[Int]'
[1, 2, 3, 4, 5, 6, 7].shuffleInPlace()
One more question: "How i make numbers[1..<4] be mutable in chaining?"
You are correct in that Nate Cook's code above has to be modified for the new ArraySlice startIndex, endIndex behaviour. I corrected the code and added some examples with comments, see below.
Regarding the things you say you don't understand: I think you need to learn more in general about things like immutable, mutable, var, let, value type, reference type, literals, etc. Here are two examples of why I think so:
1. You seem to be surprised by the fact that shuffleInPlace returns Void / (). Which shouldn't be surprising given the name and the return type of shuffleInPlace.
numbers3[1..<4].shuffleInPlace()
What happens here is that numbers3[1..<4] creates an ArraySlice that is then mutated by shuffleInPlace and then thrown away into empty space. I'm not sure what you wanted to accomplish here, maybe you meant to do something like this:
let shuffledSlice = numbers3[1..<4].shuffled()
2. You're trying to use the mutating method shuffleInPlace on an array literal, but of course that won't work.
[1, 2, 3, 4, 5, 6, 7].shuffleInPlace()
Array literals are immutable and you are trying to call a mutating method on it, and the compiler is only doing its job telling you that you can't do that.
You can read the Swift iBook and watch related WWDC videos to learn more about these things and more.
Here's the working code example:
import Foundation
extension CollectionType {
/// Return a copy of `self` with its elements shuffled
@warn_unused_result(mutable_variant="shuffleInPlace")
func shuffled() -> [Generator.Element] {
var list = Array(self)
list.shuffleInPlace()
return list
}
}
extension MutableCollectionType where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffleInPlace() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in 0 ..< count - 1 {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
guard i != j else { continue }
swap(&self[startIndex + i], &self[startIndex + j]) // <-- Modified this line in order to support new behaviour of ArraySlice.
}
}
}
var numbers = [0, 1, 2, 3, 4, 5, 6] // We declare this as var as we will mutate it.
numbers.shuffleInPlace() // The array is shuffled in place, method returns Void == ().
print(numbers) // Prints the array: [3, 5, 6, 1, 4, 2, 0]
let slice = numbers[3 ..< 7] // Take a slice of the last four numbers, put it in a const/let since we do not want to mutate it.
let shuffledSlice = slice.shuffled() // Get a shuffled copy of slice, not mutating the original, returning the shuffled copy.
print(slice) // Prints [1, 4, 2, 0] which is the last four elements of numbers.
print(shuffledSlice) // Prints [4, 1, 0, 2] which are the shuffled copy of that slice.
print(numbers) // Prints [3, 5, 6, 1, 4, 2, 0] again since numbers hasn't been mutated since it was shuffled using shuffledInPlace above.