web-dev-qa-db-fra.com

Tableau aléatoire Swift 3

Comment puis-je convertir la fonction ci-dessous en Swift 3? Vous obtenez actuellement un Binary operator '..<' cannot be applied to operands of type 'Int' and 'Self.IndexDistance' Erreur.

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

29
kye

count renvoie un IndexDistance qui est le type décrivant la distance entre deux indices de collection. IndexDistance doit être un SignedInteger, mais pas nécessairement un Int et peut être différent de Index. Il n'est donc pas possible de créer la plage 0..<count - 1.

Une solution consiste à 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 également correctement avec le tableau slices (où l'indice du premier élément n'est pas nécessairement nul).

Notez que selon le nouveau "Swift API Design Guidelines" , shuffle() est le "bon" nom pour une méthode de shuffle mutant, et shuffled() pour le non - homologue mutant 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: A (encore plus général) Swift 3 a été ajoutée à Comment mélanger un tableau dans Swift? in pendant ce temps.


Pour Swift 4 (Xcode 9) , il faut remplacer l'appel à la fonction swap() par un appel à la fonction swapAt() méthode de la collection. De plus, la restriction sur le type Index 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 swapAt.


Depuis Swift 4.2 (Xcode 10, actuellement en version bêta), avec l'implémentation de SE-0202 Random Unification , shuffle() et shuffled() font partie de la bibliothèque standard Swift.

78
Martin R

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)
10
Josh Homann

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

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]
0
Daniel Illescas