SwiftでのRange


本文は主にRangeファミリークラスのいくつかの実装の詳細とSwiftにおけるプロトコルプログラミング向けのいくつかの具体的な表現を説明する.便宜上、classやstructを総称して『類』と呼ぶ.
基本的な紹介
Swift 4.0以前にRangeファミリーは4種類ありました.
let rang: Range = 0.0..<1.0 //     
let closedRange: ClosedRange = 0.0...1.0 //    
let countableRange: CountableRange = 0..<1 // Countable     
let countableClosedRange: CountableClosedRange = 0...1 // Countable    

その後、Swift 4.0には4つのタイプが追加されました.
let partialRangeThrough: PartialRangeThrough = ...1.0 //     
let partialRangeFrom: PartialRangeFrom = 0.0... //     
let partialRangeUpTo: PartialRangeUpTo = ..<1.0 //     
let countablePartialRangeFrom: CountablePartialRangeFrom  = 1... // Countable     

しかし、Swift 4.2になると5種類しか残っておらず、それぞれRangeClosedRangePartialRangeThroughPartialRangeFromPartialRangeUpTotypealias、すべてのCountableタイプが対応するStringである.
public typealias CountableRange = Range
public typealias CountableClosedRange = ClosedRange
public typealias CountablePartialRangeFrom = PartialRangeFrom

基本構成
RangeのすべてのタイプはBound汎用structを有し、このBoundはComparableプロトコルを継承しなければならない.
public struct Range where Bound : Comparable
public struct ClosedRange where Bound : Comparable
public struct PartialRangeThrough where Bound : Comparable
public struct PartialRangeFrom where Bound : Comparable
public struct PartialRangeUpTo where Bound : Comparable

このプロトコルは、swift標準ライブラリのほとんどが実装されているため、DateIndexPathおよびcontains(:)などが含まれる.
let stringRange = "a"..

カスタムクラスでRangeを作成する必要がある場合も、Comparableプロトコルを継承し、対応する方法を実装する必要があります.たとえば、
struct Foo: Comparable {
    var value: Int
    static func < (lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value < rhs.value
    }
    
    init(_ v: Int) {
        value = v
    }
}

let range = Foo(1)...Foo(20)
foo.contains(Foo(2)) // true

また、contains(:)も自動的に実現されました.これは、RangeExpressionプロトコルのおかげです.
public func contains(_ element: Self.Bound) -> Bool

その理由は、各Rangeタイプにはextensionがあるからです.汎用BoundがComparableに準拠している場合、対応するクラスを拡張して現実的なRangeExpressionプロトコルを拡張します.
extension Range : RangeExpression where Bound : Comparable 
extension ClosedRange : RangeExpression where Bound : Comparable 
extension PartialRangeThrough : RangeExpression where Bound : Comparable
extension PartialRangeFrom : RangeExpression where Bound : Comparable
extension PartialRangeUpTo : RangeExpression where Bound : Comparable

オブジェクト向けの言語で一般的にStrideableを実現する方法を考えてみましょう.
Countableの実装の詳細
前述のSwift 4.2ではすべてのCountableタイプがtypealiasであり、Countable能力があるかどうかは汎用Boundに抽象化され、ClosedRangeを例に挙げる
extension ClosedRange : Sequence where Bound : Strideable, Bound.Stride : SignedInteger {

    /// A type representing the sequence's elements.
    public typealias Element = Bound

    /// A type that provides the sequence's iteration interface and
    /// encapsulates its iteration state.
    public typealias Iterator = IndexingIterator>
}

Sequenceプロトコルを継承するために、汎用BoundはStrideableを先に継承する必要があり、Bound.Stride : SignedIntegerプロトコルは以下のように定義されている.
public protocol Strideable : Comparable {
    /// A type that represents the distance between two values.
    associatedtype Stride : Comparable, SignedNumeric

    public func distance(to other: Self) -> Self.Stride

    public func advanced(by n: Self.Stride) -> Self
}

バインドタイプStrideと実装する必要がある2つの方法があり、StrideableStrideのバインドタイプSignedIntegerを継承する必要があることを示す.
まとめてswiftは,汎用制約,プロトコルバインド型制約によりextension能力を結合し,Countable能力を汎用Boundに抽象化し,最終的に汎用BoundによってRangeがSequence能力を有するか否かを決定する.
なぜIntはCountableのRangeを作成できるのか
もしかするとあなたはIntを通じて作成したRangeしか知らないかもしれませんが、それはCountableRangeですが、なぜですか?まず、IntはFixedWidthInteger,SignedIntegerに継承される.
public struct Int : FixedWidthInteger, SignedInteger

SignedIntegerはBinaryIntegerに継承され、SignedNumeric
public protocol SignedInteger : BinaryInteger, SignedNumeric {
}

BinaryIntegerは一定の条件下でStrideableに継承される.
public protocol BinaryInteger : CustomStringConvertible, Hashable, Numeric, Strideable where Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude
BinaryIntegerペアStrideable実装の表示を続行します.
extension BinaryInteger {   
    public func distance(to other: Self) -> Int
    public func advanced(by n: Int) -> Self
}

StrideタイプがIntであり、Int自体がSignedIntegerに継承されていることがわかります.これは前述したBound.Stride : SignedIntegerの条件に合っています.最後にもう一つの限定条件を忘れないでください.
where Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude

MagnitudeはNumericプロトコルのバインドタイプであり、Numericは以下のように定義されている.
public protocol Numeric : Equatable, ExpressibleByIntegerLiteral {
    associatedtype Magnitude : Comparable, Numeric
    public var magnitude: Self.Magnitude { get }
}

しかし、BinaryIntegerには、指定されたMagnitudeのタイプのextensionは見つかりません.これはMagnitudeが具体的なクラスで指定されていることを示しているだけで、Intに戻るとやはりMagnitudeが見つかります.
public struct Int : FixedWidthInteger, SignedInteger {
    public typealias Magnitude = UInt
}

引き続きUIntの表示
public struct UInt : FixedWidthInteger, UnsignedInteger {
    public typealias Magnitude = UInt
}
UnsignedIntegerBinaryIntegerに継承
public protocol UnsignedInteger : BinaryInteger {
}

したがってSelf.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.MagnitudeInt.UInt : BinaryInteger, Int.UInt == Int.UInt.UIntに相当する.これでIntタイプはすべての条件を満たし、実際にはInt全体のInt家族とUInt家族タイプがこれらの条件を満たしているだけでなく、以下はIntUIntの大まかなプロトコル継承関係についてです.
                                +---------------+   
                                |  Comparable   |    
                                +-------+-------+   
                                        ^
                                        |
                +-------------+   +-----+-------+
        +------>+   Numeric   |   | Strideable  |
        |       +------------++   +-----+-------+
        |                    ^          ^
        |                    |          |
+-------+-------+        +---+----------+----+ 
| SignedNumeric |        |   BinaryInteger   | 
+------+--------+        +---+-----+-----+---+
       ^         +-----------^     ^     ^----------+        
       |         |                 |                |  
+------+---------++    +-----------+--------+  +----+-------------+
|  SignedInteger  |    |  FixedWidthInteger |  |  UnsignedInteger |  
+---------------+-+    +-+----------------+-+  +--+---------------+
                ^        ^                ^       ^
                |        |                |       |
                |        |                |       |
               ++--------+-+             ++-------+--+     
               |Int family |             |UInt family|    
               +-----------+             +-----------+

Strideableの手動実装
struct Foo {
    var value: Int
    init(_ v: Int) {
        value = v
    }
}

extension Foo: Strideable {
    func distance(to other: Foo) -> Int {
        return other.value - self.value
    }
    
    func advanced(by n: Int) -> Foo {
        var result = self
        result.value += n
        return result
    }
}

FooはStrideableを継承すると同時にそのバインドもIntに指定され、カスタムタイプのRangeを作成し、Sequenceに継承することができます.
let fooRange = Foo(1)...Foo(20)
fooRange.contains(Foo(2))
Array((Foo(1)..

まとめ
Swiftはプロトコルプログラミング向けの言語としてRangeの実現に一目瞭然であり,SE-0142,SE-0143の提案に伴い,それぞれSwift 4.0とSwift 4.2に組み込まれた後,この方面の能力が強化された.