プロトコルStringInterpolationConvertibleの使いかたがようやくわかったので簡単に紹介します。
StringInterpolationConvertibleとは
String Interpolationとは簡単に言うと、文字列の中の\(コレ)のことです。
つまり、StringInterpolationConvertibleとは、 String Interpolationを含む文字列をコンバートできるようにしたクラス、ということになります。
StringInterpolationConvertibleプロトコルの実装例
StringInterpolationConvertibleプロトコルの実装すると、次のように数値に16進数を併記させるといったことも可能になります。
print("\(777) is a magical number!" as HexNumberString)
//=> "777 (0x309) is a magical number!"
print("\(0xdead_beaf), \(UInt(777))" as HexNumberString)
//=> "3735928495 (0xDEADBEEF), 777"
実装は次のような感じ (解説は後ほど)。
final class HexNumberString : StringInterpolationConvertible, Printable {
let s: String
required init<T>(stringInterpolationSegment expr: T) {
s = "\(expr)"
}
required init(stringInterpolationSegment expr: Int) {
let hex = String(NSString(format:"%X", expr))
s = "\(expr) (0x\(hex))"
}
required init(stringInterpolation strings: HexNumberString...) {
s = "".join(strings.map { $0.s })
}
var description: String {
return s
}
}
StringInterpolationConvertibleプロトコルの処理手順
- 与えられた文字列は、String Interpolationの部分と地の文字列とに分けられます。
- 先頭から順番に
init(stringInterpolationSegment)が呼ばれて、それぞれのインスタンスが作成されます。 - 作成したインスタンスをまとめた配列で
init(stringInterpolation strings: HexNumberString...)が呼ばれます
例えば、"abc\(1 + 30)" as HexNumberStringでは、まず
String型の"abc"- (評価後の)
Int型の31 String型の""
の3つに分かれます。
1つめの"abc"と3つめの""に対してはinit<T>(stringInterpolationSegment: T)が呼ばれますが、2つめの31にはinit(stringInterpolationSegment: Int)が呼ばれます。
これによって、3つのHexNumberString型のインスタンス (内部変数sは順番に"abc", "31 (0x1F)", "") が作成されます。
そして最後に、この3つのインスタンスをまとめた配列を引数として、init(stringInterpolation strings: HexNumberString...)が呼ばれます。
StringInterpolationConvertibleプロトコルの実装の手引き
汎用型のinit<T>(stringInterpolationSegment: T)を実装する以外にも、
自分がカスタマイズしたい型をinit(stringInterpolationSegment: SomeClass)として実装します。
上の例では16進数を表示させたいInt型について実装しています。
汎用型のinit<T>(stringInterpolationSegment: T)は地のStringを含むことがあるので何もしないのが無難でしょう。
同様に、init<T>(stringInterpolationSegment: String)では地のStringを含むことがあるので注意です。
StringInterpolationConvertibleは実装クラスがfinalでないと次のようなエラーになるのでfinalしておきます。
Protocol ‘StringInterpolationConvertible’ requirement ‘init(stringInterpolation:)’ cannot be satisfied by a non-final class (‘MySample’) because it uses ‘Self’ in a non-parameter, non-result type position
Printable (Swift 2ではCustomStringConvertible) を実装しないとprintでクラス名が表示されるだけになるので実装します。
応用編
数値をローマ数字や漢数字に変換するクラスや、次のような比較用タプルが使えるようにしたDiffStringというのも考えられます。
required init<Eq: Equatable>(stringInterpolationSegment expr: (Eq, Eq)) {
let cmp = expr.0 == expr.1 ? "==" : "!="
s = "\(expr.0) \(cmp) \(expr.1)"
}
print("\(1,2)" as DiffString)
//=> 1 != 2
print("\(5,5)" as DiffString)
//=> 5 == 5
他にも便利そうな応用例がありましたら教えてください。

0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。