Co to jest kawałek w Swift?

85

Co to jest wycinek w Swift i czym różni się od tablicy?

Z dokumentacji typ podpisu indeksu dolnego (Zakres) to:

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

Dlaczego nie zwrócić innego, Array<T>a nie Slice<T>?

Wygląda na to, że mogę połączyć wycinek z tablicą:

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

Ale to daje błąd:

nie można znaleźć przeciążenia dla „indeksu dolnego”, który akceptuje podane argumenty

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

Co to jest plasterek?

hjing
źródło

Odpowiedzi:

97

Wycinek wskazuje na tablicę. Nie ma sensu tworzyć kolejnej tablicy, gdy tablica już istnieje, a wycinek może po prostu opisać pożądaną jej część.

Dodatek powoduje ukryty przymus, więc działa. Aby kontynuować pracę zadanie, ty musiałby zmusić:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
matowe
źródło
To ma sens. Czy jest to opisane gdziekolwiek w dokumentacji?
hjing
2
Tak naprawdę to szczegół implementacji. Sprytnie zbadałeś sprawę krawędzi, która ujawnia działanie czarnej skrzynki ...!
mat.
2
w rzeczywistości Slice jest kopią tablicy. Po zaktualizowaniu oryginalnej tablicy wycinek się nie zmieni. Wynika to z natury strukturalnej. A do notatki, protokół Sliceable nie implikuje użycia typu Slice
Marcin
4
Szybki język się zmienił i kawałek faktycznie używa trzech wielokropków, a nie dwóch developer.apple.com/library/ios/documentation/General/Reference/…
Daniel Galasko
7
Nie zrujnowałoby korespondencji, gdybyś dodał prostą edycję informującą nowych czytelników, że zmienili się operatorzy zakresu?
Daniel Galasko
22

Uwaga: na szczęście ta odpowiedź jest nieprawidłowa od wersji Swift beta 3, ponieważ tablice są teraz prawdziwymi typami wartości.


@matt jest poprawne, powyżej - Slice<T>punkty w tablicy. Wydaje się to sprzeczne ze sposobem, w jaki Swift obsługuje wszystkie inne typy danych, z którymi pracujemy, ponieważ oznacza to, że wartość wycinka może się zmienić, nawet jeśli jest zadeklarowana jako stała:

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

Najgorsze jest to, że wycinek działa jak tablica. Biorąc pod uwagę, że w Swift spodziewamy się niezmienności, wydaje się niebezpieczne, że indeksowane wartości wycinka mogą się zmieniać bez ostrzeżenia:

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

Ale jeśli podstawowa tablica zmieni się zbyt drastycznie, zostaną odłączone:

arr.removeAtIndex(0)                       // this detaches slice from arr
println(slice)                             // ["bonjour", "le monde"]
arr[0] = "hola"
println(slice)                             // ["bonjour", "le monde"]
Nate Cook
źródło
6
W rzeczywistości plasterki działają tak samo jak tablice, jak mówisz. Szybkie tablice mają zmienne elementy, nawet jeśli są zadeklarowane jako niezmienne . Na przykład spróbuj let arr = ["hello", "world", "goodbye"]; arr[0] = "bonjour". Przekonasz się, że to działa. W przypadku niezmiennych tablic, co dziwne, niezmienny jest tylko rozmiar , a nie zawartość. (Zobacz „Mutability of Collections” w The Swift Programming Language )
Matt Gibson,
1
„Zmienne elementy” - to już nie jest prawda
Alex Brown
14

Podsumowanie:

Powyższe odpowiedzi były prawdziwe aż do Beta 3 (i mogą ulec zmianie w przyszłych wydaniach)

Slice działa teraz jak tablica, ale jak @matt powiedział powyżej, jest faktycznie płytką kopią do tablicy pod maską, dopóki nie zostanie dokonana zmiana. Plasterki (teraz) wyświetlają migawkę oryginalnych wartości,

Zwróć również uwagę, że składnia wycinka uległa zmianie:

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

Przykład:

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"]

Pozwala to na znacznie bardziej jednolite przetwarzanie, ponieważ prostsze jest (IMHO) przetwarzanie list w stylu Pythona - filtrowanie jednej listy, aby utworzyć drugą. zgodnie z odpowiedzią Matta przed Beta 3, trzeba było utworzyć tymczasową tablicę, aby zmapować wycinek. Nowy kod jest teraz prostszy:

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

(choć żeby być uczciwym, foo to wciąż kawałek)

Odniesienie:

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

Ważne zmiany, rozwiązane problemy, - Swift Language, akapit 1

„Tablica w języku Swift została całkowicie przeprojektowana, aby mieć pełną semantykę, taką jak Dictionary i String ... m”

Chris Conover
źródło