29 votes

Shuffle array swift 3

Comment puis-je convertir la fonction ci-dessous en swift 3 ? Obtient actuellement une erreur 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])
    }
  }
}
 

référence: https://stackoverflow.com/a/24029847/5222077

78voto

Martin R Points 105727

count renvoie un IndexDistance qui est le type décrivant la distance entre deux de collecte d'indices. IndexDistanceest requises pour être un SignedInteger, mais n'a pas besoin d'être un Int et peut être différent de Index. Par conséquent, il n'est pas possible de créer la gamme 0..<count - 1.

Une solution est d'utiliser startIndex et endIndex au lieu de 0 et 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])
            }
        }
    }
}

Un autre avantage est que cela fonctionne aussi correctement avec l'ensemble des tranches (où l'indice du premier élément n'est pas nécessairement égale à zéro).

Notez que, selon la nouvelle Swift"API"instructions de Conception, shuffle() est le "bon" nom d'une mutation aléatoire de la méthode, et shuffled() pour les non-mutation homologue qui retourne un tableau:

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffled() -> [Iterator.Element] {
        var list = Array(self)
        list.shuffle()
        return list
    }
}

Mise à jour: Un (encore plus général) de Swift, la version 3 a été ajouté à Comment puis-je shuffle un tableau dans Swift? dans le même temps.


Pour Swift 4 (Xcode 9) on doit remplacer l'appel à l' swap() fonction par un appel à l' swapAt() méthode de la collection. Aussi la restriction sur l' Index type n'est plus nécessaire:

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

Voir SE-0173 Ajouter MutableCollection.swapAt(_:_:) pour plus d'informations sur l' swapAt.


Comme de Swift 4.2 (Xcode 10, actuellement en version bêta), avec la mise en œuvre de SE-0202 Aléatoire de l'Unification, shuffle() et shuffled() font partie de la Swift de la bibliothèque standard.

10voto

Josh Homann Points 7888

Il y a un remaniement des pêcheurs-yates dans Gamekit:

 import GameKit
let unshuffledArray = [1,2,3,4]
let shuffledArray = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: unshuffledArray)
print(shuffledArray)
 

Vous pouvez également transmettre et stocker une graine aléatoire, de sorte que vous obtenez la même séquence de valeurs de mélange pseudo-aléatoire chaque fois que vous fournissez la même graine au cas où vous auriez besoin de recréer une simulation.

 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)
 

8voto

matt Points 60113

Je suggérerais simplement de mélanger les tableaux au lieu d'essayer d'étendre cela aux collections en général:

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

0voto

Daniel Illescas Points 1709

Vous pouvez utiliser l'extension NSArray du framework GameplayKit pour cela:

 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]
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X