きももまなきんのダンス

ぼちぼちと、技術的なあれこれを。

try! Swiftメモ: Day2-2 誰もが知りたいSequenceとCollectionのすべて

SequenceとCollectionについてのセッションメモ+整理です。

例として挙げられたコードも一部書き起こしています。

Sequence and Collection

今日は特にprotocolについて話したい

概要

Sequence? * Arrayなどと性質は同じ * シンプルで柔軟なので協力

  • Sequence
    • -> Collection
      • -> BidirectionalCollection
        • -> BidirectionalCollection

という関係になっている

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

スポンサードリンク