【Swift】コレクションについて〜範囲〜

#範囲型 とは

配列型とは、主にRange\<Bound>型のことを表しますが、
その他にも様々な範囲型がswiftには用意されています。

それらの型には、末尾を含まない`..<演算子`と末尾を含む`...演算子`が存在します。
演算子の他にも、両端の境界の有無や、カウント可能かどうかによって分類されます。

| 囲型 | 境界 | カウント可能 |
|:-:|:-:|:-:|
| **..<演算子(終了の値を含まない)**  |  |  |
| Range\<Bound>  | 両端  |  不可能 |
| CountableRange\<Bound>  |  両端 |  可能 |
| PartialRangeUpTo\<Bound>  | 末尾のみ  | 不可能  |
| **...演算子(終了の値含む)**  |   |   |
|  ClosedRange\<Bound> | 両端  | 不可能  |
| CountableClosedRange\<Bound>  | 両端  | 可能  |
| PartialRangeThrough\<Bound>  |  末尾のみ |  不可能 |
| PartialRangeFrom\<Bound>  | 先頭のみ  | 不可能  |
| CountablePartialRangeFrom\<Bound>  | 先頭のみ  | 可能  |

カウント可能な範囲とは、
Int型のように数えられる範囲のことをカウント可能な範囲といいます。

範囲型のBoundは、プレースホルダ型となっており、
使用する際は、Range\<Float>型やCountableClosedRange\<Int>型のように、
**Bound型に具体的な型を指定して使用します。**

##範囲演算子

先ほど記載しましたが、範囲演算子は２種類存在します。
末尾を含まない`..<演算子`と、末尾を含む`...演算子`の２種類です。
どちらの演算子を使用した場合も先頭の値は範囲に含まれます。

###..<演算子
末尾の値を含まない範囲を表す型には、
`Range<Bound>型`、`CountableRange<Bound>型`、`PartialRangeUpTo<Bound>型`の
３つの型が存在します。

Range\<Bound>型とCountableRange\<Bound>型を生成するには、
左辺に先頭の値、右辺に末尾の値を指定する必要があります。
**また、右辺と左辺がInt型の場合はカウント可能なのでCountableRange\<Bound>型になります。**

```swift

let range1 = 1 ..< 4   // CountableRange<Int>型
let range2 = 1.0 ..< 4.0   // Range<Double>型
```

カウント可能なCountableRange\<Bound>型は、
要素を列挙するためのプロトコルであるSequenceプロトコルに準拠しています。

**Sequenceプロトコルに準拠している型は、
for-in文を使いその要素に対して順次アクセスできます。**

```swift

let range = 1 ..< 4   // CountableRange(1..<4)

for value in range {
   print(value)
}

//実行結果
1
2
3
```
for-in文に関しては別の記事で詳しく書いていきますが、
rangeの範囲だけ{ }内の処理を繰り返します。

この時にカウント可能でないと範囲を正確に判断できないので、
Sequenceプロトコルに準拠しているCountableRange\<Bound>型のみがfor-in文を使用できます。


また、..<演算子の左辺の値を指定しない場合はPartialRangeUpTp\<Bound>型になります。
この型は、右辺の値未満という範囲を表します。

今回の場合ですと、４未満ということになります。

```swift

let range = ..<4   // PartialRangeUpTo<Int>
```


##...演算子
末尾を含む範囲を表すには、
`ClosedRange<Bound>型`、`CountableClosedRange<Bound>型`
`PartialRangeThrough<Bound>型`、`PartialRangeFrom<Bound>型`
`CountablePartialRangeFrom<Bound>型`の５つが存在します。

ClosedRange<Bound>型とCountableClosedRange<Bound>型を生成するには、
左辺に先頭の値、右辺に末尾の値を記述する必要があります。

...演算子の両辺がInt型の場合はカウント可能なので、
CountableClosedRange\<Bound>型になります。

**また、..<演算子と同じく、カウント可能なCountableClosedRange\<Bound>型は、
Sequenceプロトコルに準拠しているためfor-in文で要素に順次アクセスできます。**

```swift

let range1 = 1.0 ... 4.0   // ClosedRange<Double>型
let range2 = 1 ... 4   // CountableClosedRange<Int>型

for value in range2 {
   print(value)
}

実行結果
1
2
3
4
```

...演算子の左辺の値を指定しない場合は、
`PartialRangeThrough<Bound>型`を生成することができます。
PartialRangeThrough\<Bound>型は、右辺の値以下という意味を持ちます。

逆に右辺の値を指定しない場合は、
`PartialRangeFrom<Bound>型`を生成することができます。
PartialRangeFrom\<Bound>型は、左辺の値以上という意味を持ちます。

また、他の範囲型と同様に、カウント可能なInt型で生成された場合は、
`CountablePartialRangeFrom<Bound>型`が生成されます。


###型推論

..<演算子や...演算子によって範囲型を生成する場合、
そのプレースホルダ型Boundは、両辺の値から型を推論します。

```swift

let intRange = 1 ..< 3   // CountableRange<Int>型
let doubleRange = 1.0 ..< 4.0   // Range<Double>型
```

もちろん型を明示的に宣言することもできます。

```swift

let floatRange: Range<Float> = 1 ..< 3   // Range<Float>型
```

###境界に使用可能な型

範囲の先頭と末尾の型を表すプレースホルダ型のBound型は、
**大小関係を比較するためのプロトコルである
Comparableプロトコルに準拠している必要があります。**

Comparableプロトコルに準拠している型といえば、
Int型やDouble型、String型などがあります。

両辺に値を指定する場合は、値の型がBound型という一つのプレースホルダ型で表されるため、
２つの値は同じ型である必要があります。

先頭の型がInt型であれば、末尾の型もInt型でないとコンパイルエラーが発生します。

```swift

let range1 = 1 ..< 2   // CountableRange<Int>型
let range2 = 1 ..< "a"   // コンパイルエラー
```

##範囲型の操作

範囲型の境界値には、`lowerBound`と`upperBound`を使用してアクセスします。

lowerBoundプロパティは範囲の先頭の値を、
upperBoundプロパティは範囲の末尾の値をそれぞれのBound型の値として返します。

```swift

let range 0 ... 10   // CountableRange(0...10)
range.lowerBound   // 0
range.upperBOund   // 10
```

片側範囲の場合は下記のようになります。

```swift

let range = ...5   // PartialRangeThorough(...5)
range.lowerBound   // 存在しないためコンパイルエラー
range.upperBound   // 5
```

**値が範囲に含まれているかどうかの判定方法も存在します。**

範囲方の値が表現する範囲内に、
特定の値が含まれているかどうか判断する`contain(_:)`メソッドを使用します。

contain(_:)メソッドは、指定された範囲内に値が含まれているかどうかを**Bool型の値で返します。**

```swift

let range = 1 ... 4   // CountableClosedRange(1...4)
range.contain(2)   // true
range.contain(10)   //false
```

これらが、範囲型の操作になります。


範囲型は、for-in文などでよく使うと思いますので、
覚えておいた方がいいと思います！

いろいろな型が存在してややこしいですが頑張りましょう・・・！

以上、最後までご覧いただきありがとうございました。