Shuffle array swift 3

Как преобразовать функцию ниже в swift 3? В настоящее время возникает ошибка Binary operator '..<' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'.

extension MutableCollection 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 { //error takes place here
      let j = Int(arc4random_uniform(UInt32(count - i))) + i
      guard i != j else { continue }
      swap(&self[i], &self[j])
    }
  }
}

ссылка: qaru.site/info/30347/...

Ответ 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.

Ответ 2

В 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)

Ответ 3

Я бы предложил просто перетасовывать массивы вместо того, чтобы распространять это на коллекции в целом:

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])
        }
    }
}

Ответ 4

Вы можете использовать расширение 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]