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 のインデックス型ではできませんが、BidirectionalIndexTypeRandomAccessIndexType では、次のようにpredecessorメソッド を使います。

let previousIndex = index.predecessor()

インデックスをまとめて移動する

インデックスを指定した数だけ進めたいときは、advance関数 を使います。

let nextIndex = advance(index, 5)

指定した数だけ戻したい場合も同様です。

ForwardIndexType のインデックス型ではできませんが、BidirectionalIndexTypeRandomAccessIndexType では次のように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 の場合は、そこに到達した時点で打ち切る(終点を含まない)範囲 (StrideTo) が得られます。ここがthrough の場合は、そこを通り過ぎた時点で打ち切る(終点ちょうどまでを含む)範囲 (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 比較する右辺値です。

左辺値と右辺値の大小関係を比較します。