У цій відповіді детально описано, як переміщуватися за допомогою швидкого та рівномірного алгоритму (Fisher-Yates) у Swift 4.2+ та як додати ту саму особливість у різні попередні версії Swift. Іменування та поведінка для кожної версії Swift відповідає мутуючим та немутуючим методам сортування для цієї версії.
Швидкий 4.2+
shuffle
і shuffled
є рідним стартовим Swift 4.2. Приклад використання:
let x = [1, 2, 3].shuffled()
// x == [2, 3, 1]
let fiveStrings = stride(from: 0, through: 100, by: 5).map(String.init).shuffled()
// fiveStrings == ["20", "45", "70", "30", ...]
var numbers = [1, 2, 3, 4]
numbers.shuffle()
// numbers == [3, 2, 1, 4]
Швидкі версії 4.0 та 4.1
Ці розширення додають shuffle()
метод до будь-якої колекції, що змінюється (масиви та небезпечні буфери, що змінюються) та shuffled()
метод до будь-якої послідовності:
extension MutableCollection {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
// Change `Int` in the next line to `IndexDistance` in < Swift 4.1
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Element] {
var result = Array(self)
result.shuffle()
return result
}
}
Таке ж використання, як у наведених вище прикладах Swift 4.2.
Швидкий 3
Ці розширення додають shuffle()
метод до будь-якої колекції, що змінюється, і shuffled()
метод у будь-якій послідовності:
extension MutableCollection where Indices.Iterator.Element == Index {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
// Change `Int` in the next line to `IndexDistance` in < Swift 3.2
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
guard d != 0 else { continue }
let i = index(firstUnshuffled, offsetBy: d)
self.swapAt(firstUnshuffled, i)
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Iterator.Element] {
var result = Array(self)
result.shuffle()
return result
}
}
Таке ж використання, як у наведених вище прикладах Swift 4.2.
Швидкий 2
(застаріла мова: ви не можете використовувати Swift 2.x для публікації на iTunes Connect з липня 2018 року)
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 startIndex ..< endIndex - 1 {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
guard i != j else { continue }
swap(&self[i], &self[j])
}
}
}
extension CollectionType {
/// Return a copy of `self` with its elements shuffled.
func shuffle() -> [Generator.Element] {
var list = Array(self)
list.shuffleInPlace()
return list
}
}
Використання:
[1, 2, 3].shuffle()
// [2, 3, 1]
let fiveStrings = 0.stride(through: 100, by: 5).map(String.init).shuffle()
// ["20", "45", "70", "30", ...]
var numbers = [1, 2, 3, 4]
numbers.shuffleInPlace()
// [3, 2, 1, 4]
Швидкий 1.2
(застаріла мова: ви не можете використовувати Swift 1.x для публікації в iTunes Connect з липня 2018 року)
shuffle
як метод мутації масиву
Це розширення дозволить перемістити змінений Array
екземпляр на місці:
extension Array {
mutating func shuffle() {
if count < 2 { return }
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&self[i], &self[j])
}
}
}
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.shuffle() // e.g., numbers == [6, 1, 8, 3, 2, 4, 7, 5]
shuffled
як немутуючий метод масиву
Це розширення дозволить отримати перетасовану копію Array
екземпляра:
extension Array {
func shuffled() -> [T] {
if count < 2 { return self }
var list = self
for i in 0..<(list.count - 1) {
let j = Int(arc4random_uniform(UInt32(list.count - i))) + i
swap(&list[i], &list[j])
}
return list
}
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let mixedup = numbers.shuffled() // e.g., mixedup == [6, 1, 8, 3, 2, 4, 7, 5]