try! Swiftメモ: Day2-2 誰もが知りたいSequenceとCollectionのすべて
SequenceとCollectionについてのセッションメモ+整理です。
例として挙げられたコードも一部書き起こしています。
Sequence and Collection
今日は特にprotocolについて話したい
概要
Sequence? * Arrayなどと性質は同じ * シンプルで柔軟なので協力
- Sequence
- -> Collection
- -> BidirectionalCollection
- -> BidirectionalCollection
- -> BidirectionalCollection
- -> Collection
という関係になっている
Sequenceについて
- 有限もあり、無限もある
Listを表現するもの
Sequenceには、associationType, makeIterator の2つのコンポーネントがある
Iteratorについて
- Iteratorは、Elementとnext()を持っている
protocol IteratorProtocol { associationType Element mutating func next() -> Element? }
LinkedListを作ってみよう。
let a = ["A", "B", "C"]
があるとする。
必要となるのは
indirect enum LinkListNode<T> { case value(element: T, next: LinkListNode<T>) case end }
これを拡張して、
↓
extension LinkListNode: Sequence { func nextIterator() -> LinkListIterator { return LinkListIterator(current: self) } }
Iteratorを実装する ↓
struct LinkListIterator<T>: IteratorProtocol { var current: LinkListNode<T> mutating func next() -> T? { switch current { case let .value(element, next): return element case .end: return nil } } }
e.g. 使ってみると、
let iterator = LinkListIterator<String>(current: LinkList) print(iterator.next()) print(iterator.next()) print(iterator.next()) print(iterator.next())
Sequence#count(predicate: () -> Bool) -> Int を実装する
let numberOfAdmins = users.filter({ $0.isAdmin }).count // この書き方より、 ↓ let numberOfAdmins = users.count({ $0.isAdmin }) // こっちのほうがいいよね // それをするための拡張 extension Sequence { func count(_ shouldCount: (Iterator.Element) -> Bool) -> Int { var count = 0 for element in self { if shouldCount(element) { count += 1 } } return count } }
predicate関数を受け取るかたちのほうが表現力が高い
Each Pairを実装してみよう
eachPairを実行すると、このように配列を操作できる
[A, B, C, D] -> [A, B], [B, C], [C, D]
e.g. 使い所はこんな感じ
普通に使うとこう
zip(sequence, sequence.dropFirst()) // Sequence<(T, T)>
eachPairを使うとすごくシンプルに書けるようになる
sequence.eachPair() extension Sequence where Self.SubSequence: Sequence, Self.SubSequence.Iterator.Element == Self.Iterator.Element { func eachPair() -> AnySequence<(Iterator.Element, Iterator.Element)> { return AnySequence(zip(self, self.dropFirst())) } }
Collection
全て有限で、複数回イテレート可能
Protocolはこんな感じ。
protocol Collection: Sequence { associationType Index: Comparable var startIndex: Int { get } var endIndex: Int { get } subscript(position: Int) -> Iterator.Element { get } }
struct APIErrorCollection { fileprivate let _errors: [APIError] } APIErrorをCollectionにしたいとき extension APIErrorCollection: Collection { // 追いつかず.. }
BidirectionalCollection
後ろ戻りできるコレクション
indexBefore
を実装する
最後まで行って、ひとつもどって、値を戻す
var last: Iterator.Element? { guard !self.isEmpty else { return nil } let indexOfLastItem = self.index(before: self.lastIndex) return indexOfLastItem }
others
他にも色々あるよ
ドキュメントに説明とかあるから見てね
- MutableCollection
- RandomAccessCollection
- RangeReplacableCollection