Что такое срез в Swift?

85

Что такое срез в Swift и чем он отличается от массива?

Из документации подпись типа нижнего индекса (Диапазон):

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

Почему бы не вернуть другой, Array<T>а не a Slice<T>?

Похоже, я могу объединить срез с массивом:

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

Но это дает ошибку:

не удалось найти перегрузку для 'индекса', которая принимает предоставленные аргументы

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

Что такое ломтик?

hjing
источник

Ответы:

97

Срез указывает на массив. Нет смысла создавать другой массив, когда массив уже существует, и срез может просто описывать желаемую его часть.

Добавление вызывает неявное принуждение, поэтому оно работает. Чтобы ваше задание работало, вам нужно будет принуждать:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
матовый
источник
Это имеет смысл. Это описано где-нибудь в документации?
hjing 06
2
На самом деле это деталь реализации. Вы ловко исследовали крайний случай, который раскрывает работу черного ящика ...!
Matt
2
на самом деле Slice - это копия массива. После обновления исходного массива срез не изменится. Это из-за структурной природы. И для примечания: протокол Sliceable не подразумевает использование типа Slice
Марчин,
4
Язык Swift изменился, и в срезе на самом деле используются три многоточия, а не два developer.apple.com/library/ios/documentation/General/Reference/…
Даниэль Галаско
7
Если бы вы добавили простое редактирование, информирующее новых читателей о том, что операторы диапазона изменились, это не нарушило бы переписку?
Даниэль Галаско
22

Примечание: этот ответ, к счастью, недействителен для Swift beta 3, поскольку теперь массивы являются типами истинных значений.


@matt правильный, вверху - Slice<T>точки в массиве. Это кажется противоречащим тому, как Swift обрабатывает все другие типы данных, с которыми мы работаем, поскольку это означает, что значение среза может измениться, даже если оно объявлено как константа:

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

Хуже всего то, что срез действует как массив. Учитывая, что в Swift у нас есть ожидание неизменности, кажется опасным, что индексированные значения среза могут измениться без предупреждения:

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

Но если базовый массив изменяется слишком резко, они отключаются:

arr.removeAtIndex(0)                       // this detaches slice from arr
println(slice)                             // ["bonjour", "le monde"]
arr[0] = "hola"
println(slice)                             // ["bonjour", "le monde"]
Нейт Кук
источник
6
Фактически, срезы работают так же, как и массивы, как вы говорите. В массивах Swift есть изменяемые элементы, даже если они объявлены неизменными . Например, попробуйте let arr = ["hello", "world", "goodbye"]; arr[0] = "bonjour". Вы обнаружите, что это работает. В случае с неизменяемыми массивами, как ни странно, неизменен только размер , а не содержимое. (См. «Изменчивость коллекций» в языке программирования Swift )
Мэтт Гибсон,
1
«Мутабельные элементы» - это уже неправда
Алекс Браун
14

Резюме:

Вышеупомянутые ответы были верны до Beta 3 (и могут снова измениться в будущих выпусках)

Slice теперь действует так же, как массив, но, как @matt сказал выше, фактически является неглубокой копией массива под капотом, пока не будут внесены изменения. Срезы (сейчас) видят снимок исходных значений,

Также обратите внимание, что синтаксис среза изменился:

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

Пример:

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

Это обеспечивает гораздо более единообразную обработку, поскольку проще (IMHO) выполнять обработку списков в стиле Python - фильтровать один список для создания другого. согласно ответу Мэтта до Beta 3 вам нужно было создать временный массив, чтобы сопоставить срез. Новый код стал проще:

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

(хотя, честно говоря, foo по-прежнему кусок)

Справка:

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

Важные изменения, проблемы решены, - Swift Language, параграф 1

«Массив в Swift был полностью переработан, чтобы иметь полноценную семантику, такую ​​как Dictionary и String ... m»

Крис Коновер
источник