SwiftでのRange
本文は主にRangeファミリークラスのいくつかの実装の詳細とSwiftにおけるプロトコルプログラミング向けのいくつかの具体的な表現を説明する.便宜上、classやstructを総称して『類』と呼ぶ.
基本的な紹介
Swift 4.0以前にRangeファミリーは4種類ありました.
その後、Swift 4.0には4つのタイプが追加されました.
しかし、Swift 4.2になると5種類しか残っておらず、それぞれ
基本構成
RangeのすべてのタイプはBound汎用structを有し、このBoundはComparableプロトコルを継承しなければならない.
このプロトコルは、swift標準ライブラリのほとんどが実装されているため、
カスタムクラスでRangeを作成する必要がある場合も、Comparableプロトコルを継承し、対応する方法を実装する必要があります.たとえば、
また、
その理由は、各Rangeタイプにはextensionがあるからです.汎用BoundがComparableに準拠している場合、対応するクラスを拡張して現実的なRangeExpressionプロトコルを拡張します.
オブジェクト向けの言語で一般的に
Countableの実装の詳細
前述のSwift 4.2ではすべてのCountableタイプがtypealiasであり、Countable能力があるかどうかは汎用Boundに抽象化され、ClosedRangeを例に挙げる
Sequenceプロトコルを継承するために、汎用Boundは
バインドタイプStrideと実装する必要がある2つの方法があり、
まとめてswiftは,汎用制約,プロトコルバインド型制約によりextension能力を結合し,Countable能力を汎用Boundに抽象化し,最終的に汎用BoundによってRangeがSequence能力を有するか否かを決定する.
なぜIntはCountableのRangeを作成できるのか
もしかするとあなたはIntを通じて作成したRangeしか知らないかもしれませんが、それは
SignedIntegerは
BinaryIntegerは一定の条件下で
StrideタイプがIntであり、Int自体が
Magnitudeは
しかし、
引き続きUIntの表示
したがって
Strideableの手動実装
FooはStrideableを継承すると同時にそのバインドもIntに指定され、カスタムタイプのRangeを作成し、Sequenceに継承することができます.
まとめ
Swiftはプロトコルプログラミング向けの言語としてRangeの実現に一目瞭然であり,SE-0142,SE-0143の提案に伴い,それぞれSwift 4.0とSwift 4.2に組み込まれた後,この方面の能力が強化された.
基本的な紹介
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種類しか残っておらず、それぞれ
Range
、ClosedRange
、PartialRangeThrough
、PartialRangeFrom
、PartialRangeUpTo
、typealias
、すべての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標準ライブラリのほとんどが実装されているため、
Date
、IndexPath
および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つの方法があり、
Strideable
はStride
のバインドタイプ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
}
UnsignedInteger
BinaryIntegerに継承public protocol UnsignedInteger : BinaryInteger {
}
したがって
Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude
はInt.UInt : BinaryInteger, Int.UInt == Int.UInt.UInt
に相当する.これでIntタイプはすべての条件を満たし、実際にはInt全体のInt家族とUInt家族タイプがこれらの条件を満たしているだけでなく、以下はInt
とUInt
の大まかなプロトコル継承関係についてです. +---------------+
| 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に組み込まれた後,この方面の能力が強化された.