Подтвердить что ты не робот

Почему функция reverse() в стандартной библиотеке Swift возвращает ReverseRandomAccessCollection?

Теперь, когда я научился Swift (на разумном уровне), я пытаюсь справиться со стандартной библиотекой, но на самом деле это в основном ελληνικά для меня!

Итак, конкретный вопрос: у меня есть массив строк, и я могу вызвать reverse() на нем.

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

Теперь наивно я подумал, что я вернусь от этого типа Массив. (Ruby, например, имеет аналогичный метод, по которому вы передаете массив и возвращаете массив)

Но теперь arr имеет тип

ReverseRandomAccessCollection<Array<String>>

который на самом деле является структурой, которая соответствует CollectionType:

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

Это означает, что я могу это сделать:

for item in arr {
  print(item)
}

но я не могу сделать

print(arr[0])

Почему это так должно быть?

Словари в Swift также реализуют CollectionType, поэтому я могу это сделать:

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

Но словари не упорядочены, как массивы, поэтому почему я могу вызвать reverse() на dicts?

Бонусные баллы, если кто-нибудь может указать мне в направлении, где я могу читать и улучшать свой Swift stdlib foo, Ευχαριστώ!

4b9b3361

Ответ 1

Это оптимизация производительности для времени и памяти. Элемент ReverseRandomAccessCollection представляет элементы исходный массив в обратном порядке, без необходимости создания нового массива и копирование всех элементов (пока исходный массив не мутировали).

Вы можете получить доступ к обратным элементам с помощью индексов:

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

или

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

Вы также можете создать массив с помощью

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

Словарь также представляет собой последовательность пар ключ/значение. В

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

вызывается совершенно другой метод reversed():

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

В результате получается массив с парами Key/Value словаря в обратном порядке. Но это ограниченное использование, потому что заказ пар "ключ/значение" в словаре может быть произвольным.

Ответ 2

Из языковых документов ReverseCollention (результат .reverse()):

Метод reverse() всегда ленив, когда применяется к коллекции с двунаправленные индексы, но не подразумевает предоставления лени алгоритмы, применяемые к его результату.

Другими словами, для обычных коллекций c, имеющих двунаправленные индексы:

  • c.reverse() делает не создавать новое хранилище

...

Следовательно, вы могли бы видеть ваш ReverseRandomAccessCollection как обертку произвольного доступа по вашему еще не обратившемуся массиву (т.е. ваш исходный массив arr еще не был скопирован и изменен на новую позицию в памяти).

Естественно, из вышесказанного вы не можете напрямую индексировать обратную коллекцию, так как Array предоставляет доступ как указатель на память, содержащую массив, и индексирование соответствует продолжению поразрядного (в зависимости от типа) вперед в Память. Тем не менее мы можем получить доступ к элементам "обратного массива" в стиле индекса массива с помощью 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"

И наоборот, мы можем явно выделить память для нашего обратного массива и рассматривать ее так же, как и любой другой массив. На этом этапе arrReverse представляет собой отдельный массив, чем arr, и не имеет никакого отношения к первому другому, кроме (один раз), создаваемому с его использованием.

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"

Мартин R победил меня, поэтому см. его заметку о словарях.

Ответ 3

С Swift 3.0 вы можете напрямую получить доступ к значению массива через индекс.

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

Это напечатает "Chesapeake"