Qu'est-ce qu'une tranche dans Swift?

85

Qu'est-ce qu'une tranche dans Swift et en quoi diffère-t-elle d'un tableau?

D'après la documentation, la signature de type de l'indice (Range) est:

subscript(Range<Int>) -> Slice<T>

Pourquoi ne pas en retourner un autre Array<T>plutôt qu'un Slice<T>?

Il semble que je puisse concaténer une tranche avec un tableau:

var list = ["hello", "world"]
var slice: Array<String> = [] + list[0..list.count]

Mais cela donne l'erreur:

Impossible de trouver une surcharge pour 'indice' qui accepte les arguments fournis

var list = ["hello", "world"]
var slice: Array<String> = list[0..list.count]

Qu'est-ce qu'une tranche?

hjing
la source

Réponses:

97

La tranche pointe dans le tableau. Inutile de créer un autre tableau lorsque le tableau existe déjà et que la tranche peut simplement en décrire la partie souhaitée.

L'addition provoque une coercition implicite, donc ça marche. Pour que votre mission fonctionne, vous devez forcer:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
mat
la source
Ça a du sens. Est-ce décrit quelque part dans la documentation?
hjing
2
C'est vraiment un détail de mise en œuvre. Vous avez savamment sondé le boîtier de bord qui révèle le fonctionnement de la boîte noire ...!
mat
2
en fait, Slice est une copie du tableau. Après avoir mis à jour le tableau d'origine, la tranche ne changera pas. Ceci est dû à la nature de la structure. Et pour la note, le protocole Sliceable n'implique pas l'utilisation du type Slice
Marcin
4
Le langage Swift a changé et une tranche utilise en fait trois points de suspension et non deux developer.apple.com/library/ios/documentation/General/Reference/…
Daniel Galasko
7
Cela ne gâcherait pas la correspondance si vous ajoutiez une simple modification informant les nouveaux lecteurs que les opérateurs de plage ont changé?
Daniel Galasko
22

Remarque: Cette réponse est heureusement invalide à partir de Swift beta 3, car les tableaux sont désormais de vrais types de valeurs.


@matt est correct, au-dessus - les Slice<T>points dans le tableau. Cela semble contraire à la façon dont Swift gère tous les autres types de données avec lesquels nous travaillons, car cela signifie que la valeur de la tranche peut changer même si elle est déclarée comme constante:

var arr = ["hello", "world", "goodbye"]    // ["hello", "world", "goodbye"]
let slice = arr[0..2]                      // ["hello", "world"]
arr[0] = "bonjour"
println(slice)                             // ["bonjour", "world"]

Le pire, c'est que la tranche agit comme un tableau. Étant donné que dans Swift, nous avons une attente d'immuabilité, il semble dangereux que les valeurs indicées de la tranche puissent changer sans avertissement:

println(slice[1])                          // "world"
arr[1] = "le monde"
println(slice[1])                          // "le monde"

Mais si le tableau sous-jacent change trop radicalement, ils sont décrochés:

arr.removeAtIndex(0)                       // this detaches slice from arr
println(slice)                             // ["bonjour", "le monde"]
arr[0] = "hola"
println(slice)                             // ["bonjour", "le monde"]
Nate Cook
la source
6
En fait, les tranches fonctionne tout comme les tableaux, comme vous le dites. Les tableaux Swift ont des éléments mutables, même s'ils sont déclarés immuables . Par exemple, essayez let arr = ["hello", "world", "goodbye"]; arr[0] = "bonjour". Vous trouverez que cela fonctionne. Avec les tableaux immuables, curieusement, seule la taille est immuable, pas le contenu. (Voir «Mutabilité des collections» dans The Swift Programming Language )
Matt Gibson
1
"Éléments mutables" - Ce n'est plus vrai
Alex Brown
14

Sommaire:

Les réponses ci-dessus étaient vraies jusqu'à la bêta 3 (et pourraient changer à nouveau dans les versions futures)

Slice agit maintenant comme un tableau, mais comme @matt l'a dit ci-dessus, est en fait une copie superficielle dans un tableau sous le capot, jusqu'à ce qu'un changement soit apporté. Les tranches (maintenant) voient un instantané des valeurs d'origine,

Notez également que la syntaxe des tranches a changé:

[from..upToButNotIncluding] -> [from..<upToButNotIncluding]

Exemple:

var arr = ["hello", "world", "goodbye"] // ["hello", "world", "goodbye"]
var arrCopy = arr
let slice = arr[0..<2]                  // ["hello", "world"]
arr[0] = "bonjour"
arr                                     // ["bonjour", "world", "goodbye"]
arrCopy                                 // ["hello", "world", "goodbye"]
slice                                   // ["hello", "world"]

Cela permet un traitement beaucoup plus uniforme, car il est plus simple (IMHO) de faire un traitement de liste de style python - filtrer une liste pour en créer une autre. selon la réponse de Matt avant la bêta 3, vous deviez créer un tableau temporaire afin de mapper une tranche. Le nouveau code est désormais plus simple:

class NameNumber {
    var name:String = ""
    var number:Int = 0

    init (name:String, number:Int) {
        self.name = name
        self.number = number
    }
}

var number = 1
let names = ["Alan", "Bob", "Cory", "David"]
let foo = names[0..<2].map { n in NameNumber(name:n, number:number++) }
foo     // [{name "Alan" number 1}, {name "Bob" number 2}]

(bien que pour être honnête, foo est toujours une tranche)

Référence:

http://adcdownload.apple.com//Developer_Tools/xcode_6_beta_3_lpw27r/xcode_6_beta_3_release_notes__.pdf

Changements importants, problèmes résolus, - Swift Language, paragraphe 1

"Array in Swift a été complètement repensé pour avoir une sémantique pleine valeur comme Dictionary et String ... m"

Chris Conover
la source