Swift のインデックス型を理解する
Swift プログラミング
Swift では何かとお世話になるインデックス型ですけど、慣れるまでは勝手が分からず戸惑ったので、その使い方と、用意されている機能を整理してみました。
Swift言語 でコレクションから特定の要素を取り出すときに、主にインデックス型 を使用します。
Array<T>型 であればインデックス型はInt型 なので扱いが簡単ですが、たとえばString型 などではForwardIndexTypeプロトコル に準拠した独自の型が使われています。
インデックス型
インデックス型は、以下のいずれかのプロトコルを継承して作られます。
プロトコル | 用途 |
---|---|
ForwardIndexType | 前方へひとつ移動が可能なインデックスです。 |
BidirectionalIndexType | 前方と後方へひとつ移動可能なインデックスです。ForwardIndexType に準拠しています。 |
RandomAccessIndexType | インデックスによる自由移動が可能なインデックスです。BidirectionalIndexType に準拠しています。 |
Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Bit 型も RandomAccessIndexType に準拠しています。
インデックス型は何かと使う機会がありますが、数値のように計算できないため、慣れるまでは戸惑うかもしれません。
それでも型の役割が単純なだけあって、提供される機能も単純なので、機能を理解しておくと扱いがとても簡単なことがわかるはずです。
インデックス型を操作する
インデックス型を操作できる機能の詳細は後で紹介しますが、まずはインデックス型をどのように操作するかについて見ておきます。
インデックスの前進と後退
インデックス型は原則的に + や - といった四則演算を使った計算ができません。
そのため、たとえば変数index に格納されているインデックスをひとつ進めたいときは、足し算ではなくsuccessorメソッド を使います。
let nextIndex = index.successor()
ひとつ前に戻したい場合、ForwardIndexType
のインデックス型ではできませんが、BidirectionalIndexType
やRandomAccessIndexType
では、次のようにpredecessorメソッド
を使います。
let previousIndex = index.predecessor()
インデックスをまとめて移動する
インデックスを指定した数だけ進めたいときは、advance関数 を使います。
let nextIndex = advance(index, 5)
指定した数だけ戻したい場合も同様です。
ForwardIndexType
のインデックス型ではできませんが、BidirectionalIndexType
やRandomAccessIndexType
では次のようにadvance関数
に負の数を指定できます。
let previousIndex = advance(index, -5)
インデックスを引き算する
インデックスを引き算したいときには、それらの距離を取るdistance関数 を使います。
let dist = distance(index1, index2)
このようにすることで、変数index1
から変数index2
までの距離、すなわちindex2 - index1
の値を得られます。この関数はすべてのインデックス型で利用できます。
インデックスを使って範囲を作る
すべてのインデックス型において、ふたつのインデックスから範囲 (Range
...演算子 を使うと、左辺から右辺までの範囲を作れます。..<演算子 を使うと、左辺から右辺までの、最後の値を含まない範囲を作れます。
let closedRange = index1...index2
let halfOpenRange = index1..<index2
また、RandomAccessIndexType
に準拠したインデックス型に限り、stride関数
を使って、任意の刻みでの範囲を作れます。
let toStride:StrideTo<Index> = stride(from: index1, to: index2, by: 3)
let throughStride:StrideThrough<Index> = stride(from: index1, through: index2, by: 3)
これで、第 3 引数のby
に指定した刻み幅で、from
からto
までの範囲を作れます。
このとき、第 2 引数がto
の場合は、そこに到達した時点で打ち切る(終点を含まない)範囲 (StrideTothrough
の場合は、そこを通り過ぎた時点で打ち切る(終点ちょうどまでを含む)範囲 (StrideThrough
インデックス型の操作で使う関数や演算子
それでは、インデックス型を操作するために用意されている機能について見て行きましょう。
インデックス型は、次の関数や演算子を使って操作できます。
関数
func advance<T : ForwardIndexType>(start: T, n: T.Distance) -> T
func advance<T : ForwardIndexType>(start: T, n: T.Distance, end: T) -> T
名称 | 内容 |
---|---|
start | 始点になるインデックスを指定します。 |
n | この数だけインデックスを進めます。 |
end | 終点になるインデックスです。 |
始点から、指定したインデックスの分だけ進んだインデックスを返します。終点が指定された場合は、そこまでの移動に制限されます。それよりも大きい移動幅が指定された場合は、終点が返されます。
func distance<T : ForwardIndexType>(start: T, end: T) -> T.Distance
名称 | 内容 |
---|---|
start | 始点のインデックスです。 |
end | 終点のインデックスです。 |
始点から終点までの距離を取得します。「終点 - 始点」と似た動きをします。
演算子
すべてのインデックス型で利用可能 (Forward, Bidirectional, RandomAccess)
演算子 | 内容 |
---|---|
++ | 保持しているインデックスの値をひとつ増加させます。 |
-- | 保持しているインデックスの値をひとつ現象させます。 |
== | ふたつのインデックスが同じ値かを判定します。 |
... | 左辺から右辺までの閉じた範囲 (Range<Index>) を生成します。 |
..< | 左辺から右辺までの閉じた範囲 (Range<Index>) を生成します。ただし、インデックス型は Comparable プロトコルにも準拠している必要があります。 |
RandomAccess インデックス型だけで利用可能
演算子 | 内容 |
---|---|
< | 左辺のインデックスが右辺より小さいかを判定します。 |
<= | 左辺のインデックスが右辺以下かを判定します。 |
> | 左辺のインデックスが右辺より大きいかを判定します。 |
>= | 左辺のインデックスが右辺以上かを判定します。 |
インデックス型に用意されている機能
これらのインデックス型には次の機能が用意されています。
ForwardIndexTypeプロトコル
まずは、すべてのインデックス型で使えるForwardIndexTypeプロトコル の機能です。簡単に言うと、この型には「現在のインデックスを持ち、ひとつ先のインデックスを取得する機能」が用意されています。一致判定も可能です。
データ型
型名 | 用途 |
---|---|
Distance = Int | ふたつのインデックスがいくつ離れているかを表現する型です。 |
準拠しているプロトコル
プロトコル | 機能 |
---|---|
Equatable | ふたつの値が一致するかを判定できます。 |
_Incrementable | 内部の値をひとつ増加できます。 |
機能
func successor() -> Self
現在のインデックスをひとつ前進させたインデックスを返します。
func ==(lhs: Self, rhs: Self) -> Bool
名称 | 内容 |
---|---|
lhs | 比較する左辺値です。 |
rhs | 比較する右辺値です。 |
左辺値と右辺値とが一致した場合に true を返します。
postfix func ++<T : _Incrementable>(inout x: T) -> T
prefix func ++<T : _Incrementable>(inout x: T) -> T
インデックスをひとつ前進させます。
BidirectionalIndexTypeプロトコル
BidirectionalIndexTypeプロトコル では、上記のForwardIndexTypeプロトコル に加えて、インデックスをひとつ前に後退させる機能が用意されています。
機能
準拠しているプロトコル
プロトコル | 機能 |
---|---|
ForwardIndexType | 前進できるインデックスです。 |
Equatable | ふたつの値が一致するかを判定できます。 |
_Incrementable | 内部の値をひとつ増加できます。 |
func predecessor() -> Self
現在のインデックスをひとつ後退させたインデックスを返します。
postfix func --<T : _BidirectionalIndexType>(inout x: T) -> T
prefix func --<T : _BidirectionalIndexType>(inout x: T) -> T
インデックスをひとつ後退させます。
RandomAccessIndexTypeプロトコル
RandomAccessIndexTypeプロトコル では、上記のBidirectionalIndexTypeプロトコル が提供する前進と後退の他に、任意の幅で自由に移動できる機能が用意されています。一致判定に加え、大小関係も判定できます。
準拠しているプロトコル
プロトコル | 機能 |
---|---|
BidirectionalIndexType | 前進と後退ができるインデックスです。 |
Strideable | 任意の幅で移動可能です。 |
Comparable | ふたつの値の大小関係を比較できます。 |
Equatable | ふたつの値が一致するかを判定できます。 |
_Incrementable | 内部の値をひとつ増加できます。 |
機能
func distanceTo(other: Self) -> Distance
名称 | 内容 |
---|---|
other | 対象のインデックスです。 |
現在のインデックスから対象のインデックスまでの距離を取得します。引き算 (other - self) と似た働きをします。
func advancedBy(n: Distance) -> Self
名称 | 内容 |
---|---|
n | 移動させる幅です。 |
現在のインデックスを、指定した幅だけ移動したインデックスを取得します。足し算 (n + self) と似た働きをします。
func <=(lhs: Self, rhs: Self) -> Bool
func >=(lhs: Self, rhs: Self) -> Bool
func >(lhs: Self, rhs: Self) -> Bool
func <(lhs: Self, rhs: Self) -> Bool
名称 | 内容 |
---|---|
lhs | 比較する左辺値です。 |
rhs | 比較する右辺値です。 |
左辺値と右辺値の大小関係を比較します。