Столкнувшись с этим недавно на XCode 7.3.1 - для меня, я заметил, что использование ОЗУ будет 100% на CleanMyMac3. Проблема волшебным образом закрепилась после перезагрузки моей машины. Справедливости ради, однако, я уже пошел вперед и попробовал принятый ответ, так что вы захотите сделать то же самое, прежде чем перезапустить на всякий случай: -)
count
возвращает IndexDistance
, который является типом, описывающим расстояние между двумя индексами сбора. IndexDistance
должен быть SignedInteger
, но не обязательно должен быть Int
и может отличаться от Index
. Поэтому невозможно создать диапазон 0..<count - 1
.
Решение состоит в том, чтобы использовать startIndex
и endIndex
вместо 0
и count
:
extension MutableCollection where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in startIndex ..< endIndex - 1 {
let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
if i != j {
swap(&self[i], &self[j])
}
}
}
}
Другим преимуществом является то, что это также работает правильно со срезами массива ] (где индекс первого элемента не обязательно равен нулю).
Обратите внимание, что в соответствии с новым «Руководством по проектированию Swift API» , shuffle()
является «правильным» именем для метода с мутировавшим перемешиванием, а shuffled()
для немутирующего аналога, который возвращает array:
extension Collection {
/// Return a copy of `self` with its elements shuffled
func shuffled() -> [Iterator.Element] {
var list = Array(self)
list.shuffle()
return list
}
}
Обновление: A (еще более общая) версия Swift 3 была добавлена в Как мне переставить массив в Swift? тем временем .
Для Swift 4 (Xcode 9) необходимо заменить вызов функции swap()
на вызов метода swapAt()
коллекции. Также больше не нужно ограничение на тип Index
:
extension MutableCollection {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
for i in indices.dropLast() {
let diff = distance(from: i, to: endIndex)
let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
swapAt(i, j)
}
}
}
См. SE-0173 Добавить MutableCollection.swapAt(_:_:)
для получения дополнительной информации о swapAt
.
Начиная с Swift 4.2 (Xcode 10, в настоящее время находится в бета-версии), с реализацией SE-0202 Random Unification , shuffle()
и shuffled()
являются частью стандартной библиотеки Swift.
Я бы предложил просто перетасовать массивы вместо того, чтобы пытаться распространить это на коллекции в целом:
extension Array {
mutating func shuffle () {
for i in (0..<self.count).reversed() {
let ix1 = i
let ix2 = Int(arc4random_uniform(UInt32(i+1)))
(self[ix1], self[ix2]) = (self[ix2], self[ix1])
}
}
}
В Gamekit есть тасовка Фишера-Йейтса:
import GameKit
let unshuffledArray = [1,2,3,4]
let shuffledArray = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: unshuffledArray)
print(shuffledArray)
Вы также можете передать и сохранить случайное начальное число, так что вы получаете одинаковую последовательность значений псевдослучайного тасования при каждом подаче одного и того же начального числа. на случай, если вам понадобится воссоздать симуляцию.
import GameKit
let unshuffledArray = [1,2,3,4]
let randomSource = GKLinearCongruentialRandomSource(seed: 1)
let shuffledArray = randomSource.arrayByShufflingObjects(in: unshuffledArray)
//Always [1,4,2,3]
print(shuffledArray)
Для этого вы можете использовать расширение NSArray из каркаса GameplayKit:
import GameplayKit
extension Collection {
func shuffled() -> [Iterator.Element] {
let shuffledArray = (self as? NSArray)?.shuffled()
let outputArray = shuffledArray as? [Iterator.Element]
return outputArray ?? []
}
mutating func shuffle() {
if let selfShuffled = self.shuffled() as? Self {
self = selfShuffled
}
}
}
// Usage example:
var numbers = [1,2,3,4,5]
numbers.shuffle()
print(numbers) // output example: [2, 3, 5, 4, 1]
print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9]