web-dev-qa-db-fra.com

Pourquoi la fonction reverse () dans la bibliothèque standard Swift renvoie-t-elle ReverseRandomAccessCollection?

Maintenant que j'ai appris Swift (à un niveau raisonnable), j'essaie de me familiariser avec la bibliothèque standard, mais en réalité, c'est principalement pour moi!

Donc, une question spécifique: j'ai un tableau de chaînes et je peux appeler reverse () dessus.

let arr = ["Mykonos", "Rhodes", "Naxos"].reverse()

Maintenant, je pensais naïvement que je récupérerais un type de tableau de ce type. (Ruby, par exemple, utilise une méthode similaire pour passer un tableau et récupérer un tableau)

Mais arr est maintenant en fait de type

ReverseRandomAccessCollection<Array<String>>

qui est en fait une structure conforme à CollectionType:

public struct ReverseRandomAccessCollection<Base : CollectionType where Base.Index : RandomAccessIndexType> : _ReverseCollectionType

Cela signifie que je peux faire ceci:

for item in arr {
  print(item)
}

mais je ne peux pas faire

print(arr[0])

Pourquoi est-ce conçu pour être de cette façon?

Les dictionnaires de Swift implémentent également CollectionType. Je peux donc le faire:

let dict = ["greek" : "Swift sometimes", "notgreek" : "Ruby for this example"].reverse()

Mais les dictionnaires ne sont pas ordonnés comme des tableaux, alors pourquoi puis-je appeler reverse () sur dicts?

Des points bonus si quelqu'un peut me diriger vers l'endroit où je peux lire et améliorer mon Swift stdlib foo, Merci!

19
Brynjar

C'est une optimisation de la performance pour le temps et la mémoire. La ReverseRandomAccessCollection présente les éléments du tableau Original dans l'ordre inverse, sans qu'il soit nécessaire de créer un nouveau tableau muté).

Vous pouvez accéder aux éléments inversés avec des indices:

let el0 = arr[arr.startIndex]
let el2 = arr[arr.startIndex.advancedBy(2)]

ou

for i in arr.indices {
    print(arr[i])
}

Vous pouvez également créer un tableau explicitement avec

let reversed = Array(["Mykonos", "Rhodes", "Naxos"].reversed())

Un dictionnaire est également une séquence de paires clé/valeur. Dans

let dict = ["greek" : "Swift sometimes", "notgreek" : "Ruby for this example"].reverse()

une méthode reversed() complètement différente est appelée:

extension SequenceType {
    /// Return an `Array` containing the elements of `self` in reverse
    /// order.
    ///
    /// Complexity: O(N), where N is the length of `self`.
    @warn_unused_result
    public func reversed() -> [Self.Generator.Element]
}

Le résultat est un tableau avec les paires clé/valeur du dictionnaireen ordre inverse. Mais ceci a une utilité limitée car l’ordre Des paires clé/valeur dans un dictionnaire peut être arbitraire.

27
Martin R

D'après la documentation linguistique de ReverseCollention (résultat de .reverse()):

La méthode reverse () est toujours paresseuse lorsqu'elle est appliquée à une collection avec des indices bidirectionnels .__, mais ne confère pas implicitement de la paresse à algorithmes appliqués à son résultat.

En d'autres termes, pour les collections ordinaires c ayant des index bidirectionnels:

  • c.reverse () fait ne crée pas de nouveau stockage

...

Par conséquent, vous pouvez voir votre ReverseRandomAccessCollection comme un wrapper à accès aléatoire sur votre tableau pas encore inversé (c’est-à-dire que votre tableau original arr n’a pas encore été copié et inversé vers une nouvelle position en mémoire).

Naturellement, à partir de ce qui précède, vous ne pouvez pas indexer directement la collection inversée, car une Array donne accès en tant que pointeur à la mémoire qui contient le tableau, et l'indexation correspond à la poursuite en avant binaire (selon le type) en mémoire. Cependant, nous pouvons toujours accéder aux éléments du "tableau inversé" dans le style d'index de tableau à l'aide de ReverseRandomAccessIndex:

let arr = ["Mykonos", "Rhodes", "Naxos"]
let arrReverse = arr.reverse()
    /* ReverseRandomAccessCollection access "wrapper" over
       the 'arr' data in memory. No new storage allocated */

let myIndex = arrReverse.startIndex.advancedBy(2)
    /* bIndex type: 
       ReverseRandomAccessIndex<ReverseRandomAccessIndex<Index>> */

print(arrReverse[myIndex]) // "Mykonos"

Inversement, nous pouvons explicitement allouer de la mémoire à notre tableau inversé et le traiter comme tout autre tableau. À ce stade, la variable arrReverse est un tableau distinct de celui de arr et ne contient aucune relation avec le précédent, à l'exception de celle (créée une fois) créée à l'aide de celui-ci.

let arr = ["Mykonos", "Rhodes", "Naxos"]
let arrReverse = Array(arr.reverse())
    /* Array<String> */

let myIndex = arrReverse.startIndex.advancedBy(2)
    /* bIndex type: Int */

print(arrReverse[myIndex]) // "Mykonos"

Martin R m'a battu, alors voyez sa note concernant les dictionnaires.

3
dfri

Avec Swift 3.0, vous pouvez accéder directement à la valeur Array via index.

var streets = ["Albemarle", "Brandywine", "Chesapeake"]
streets = streets.reversed()
print("Array at index is \(streets[0])")

Ceci imprimera "Chesapeake"

1
Sabby