post Image
[Swift] シンプルなカウントアップでSequenceに強くなる

少年は言った。

「0から100までカウントアップしたい!!」

よし、じゃあ今日は0から100までの整数値を出力する、Sequenceの仲間たちを紹介するよ!
一緒にSequenceに強くなろう!:muscle:

Sequenceのチカラ

まずはSequenceとは何かということと、そのパワーについて知りましょう。

Sequenceとは

Sequenceとは、標準ライブラリのの中にある以下のようなprotocolです。

public protocol Sequence {
    public func makeIterator() -> Self.Iterator
}

makeIteratorを実装することによりSequenceに準拠することができます。Self.IteratorはIteratorProtocolを実装するクラスもしくは構造体で、makeIteratorは単にIteratorProtocolに準拠するインスタンスを返す関数です。IteratorProtocolとは以下のようなprotocolです。

public protocol IteratorProtocol {
    public mutating func next() -> Self.Element?
}

Self.Elementはイテレートする任意の要素型です。IteratorProtocolはnext()を呼ぶたびに「次の要素」を返し、最後の要素の次にnilを返すように実装します。

Sequenceに準拠するといいこと

Sequenceに準拠することにより、containsforEach, map, filter, reduceなどなど、多数の便利な関数を利用できるようになります。うまく使えば強力な独自クラスをつくることも可能です。

Sequenceとfor-in

さらに、SwiftではSequenceプロトコルに準拠することにより、for-inでイテレートできるようになります。標準ライブラリでよく使うArrayやDictionaryなどもSequenceプロトコルに準拠しているため、for-inでイテレートできるようになっています。以下の例はArrayをfor-inでイテレートしています。

let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in numbers {
    print("\(i)") // 0, 1, 2...
}

さて、ここからは0から100までを出力する様々なSequenceを作る方法を紹介していきます。

カウントアップするSequenceを作る様々な方法

Closed Range Operatorを使う

おそらく最も一般的な方法はRange Operatorを使う方法です。

for i in 0...100 {
    print("\(i)") // 0, 1, 2...
}

なにも問題ありません。シンプルで過不足無く美しいコードです。

0...100CountableClosedRangeインスタンスを生成します。これはstructであり、Sequenceに準拠しているためfor-inでイテレートできます。

SequenceとCollectionの違い

実際のところ、CountableClosedRangeは単なるSequenceではなく、さらに高機能なRandomAccessCollectionに準拠しています。SequenceとCollectionはfor-inの挙動において抑えておきたい違いが2点あります

  1. Sequenceは一度イテレートすると二回め以降は同じようにイテレートできることが保証されていませんが、Collectionは何度もイテレート可能です
  2. SequenceはIteratorの実装によっては無限に続く可能性もありますが、Collectionは添字アクセスを可能にするため、有限であることが保証されています

詳しくは公式リファレンスの “Traversing a Collection” を参照してください。
https://developer.apple.com/reference/swift/collection

Sequenceを自分で実装する

SequenceとIteratorProtocolを実装する

自分でSequenceを実装する方法をご紹介します。

struct CountUp: Sequence, IteratorProtocol {
    private var count = 0
    mutating func next() -> Int? {
        defer { count += 1 }
        return count <= 100 ? count : nil
    }
}

for i in CountUp() {
  print("\(i)") // 0, 1, 2...
}

IteratorProtocolとSequenceを別々に実装することもできますが、こちらのほうが同時に実装できてシンプルですね。しかもこの場合makeIterator()はデフォルト実装を利用できるため、自分で実装する必要はありません。

公式でもこの書き方が例として紹介されています。
https://developer.apple.com/reference/swift/sequence

AnySequenceを使う

AnySequenceを使うと、具体的なクラスを作ることなく独自処理のSequenceを実装することができます。

let countUp = AnySequence { () -> AnyIterator<Int> in
    var count = 0
    return AnyIterator {
        defer { count += 1 }
        return count <= 100 ? count : nil
    }
}

for i in countUp {
    print("\(i)") // 0, 1, 2...
}

AnySequence自体はSequenceの具体的な型を隠蔽して扱う(Type Erasure)ためのクラスですが、今回のように一時的な無名Sequenceを作る用途でも利用できます。初期化時にmakeIteratorの実装をクロージャで渡すようなイメージです。

StrideThroughを使う

StrideThroughというSequenceがあります。stride(from:through:by:)関数を用いて、指定した閉区間をストライドで区切ったSequenceを作ることができます。

let countUp = stride(from: 0, through: 100, by: 1)
for i in countUp {
    print("\(i)") // 0, 1, 2...
}

byに2を渡せば 0, 2, 4, 6… と1つ飛ばしにもできますし、stride(from: 100, through: 0, by: -1)のようにマイナスを渡せば 100, 99, 98… と減っていくようにもできます。

また、半開区間をストライドで区切るStrideToと言うものあります。そちらはstride(from:to:by:)を使います。StrideThroughと違い、toで指定した値は出力されません。

UnfoldSequenceを使う

UnfoldSequenceというSequenceがあります。カウントアップは以下のように書けます。

let countUp = sequence(first: 0) { prev -> Int? in
    return prev >= 100 ? nil : prev + 1
}

for i in countUp {
    print("\(i)") // 0, 1, 2...
}

prevは1つ前に出力した値です。UnfoldSequenceは1つ前の値を保持しており、1つ前の値から次の値を計算して出力しています。今回は単に1つ前の値に1を足して次の出力としています。

ただし、UnfoldSequence自体は単に1つ前の値だけを保持するだけのものではありません。より正確に言えば、UnfoldSequenceはイテレーションを通して状態を持ち、出力する要素を毎回計算するSequenceです。イテレーションを通して状態を持ち続けたい場合はsequence(state:next:)という関数を使います。stateには自由な値を設定でき、イテレーションを通してアクセスすることができます。

UnfoldSequenceは使いどころが難しいですが、無限に続く数列を定義したり、木構造の親を辿っていくような再帰的な処理に使ったり、2つのイテレータを交互に出力したりなど、工夫次第で様々な用途で使えるようです。

まとめ

さまざまな方法でカウントアップしてみましたが、いかがでしたでしょうか。
Sequenceに強くなれたかな?:angel:


:whale2: Twitter: @a_beco
:guitar: SoundCloud: @beco <- 作業用BGMにどうぞ!


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。