Ответ 1
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 Design Guidelines" ,
shuffle()
является "правильным" именем для мутирующего метода тасования,
и shuffled()
для не мутирующего экземпляра, который возвращает массив:
extension Collection {
/// Return a copy of `self` with its elements shuffled
func shuffled() -> [Iterator.Element] {
var list = Array(self)
list.shuffle()
return list
}
}
Обновление: добавлена (еще более общая) версия Swift 3 Как перетасовать массив в Swift? тем временем.
Для Swift 4 (Xcode 9) нужно заменить вызов на swap()
функцию вызовом метода swapAt()
коллекции.
Кроме того, ограничение типа Index
больше не требуется:
extension MutableCollection {
/// 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 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
.