本稿を書いている真っ最中の3月22日未明
Xcode 7.3 with Swift 2.2
Swift 2.
しかし無変更でコンパイルが通るとはいえ、
typealias UIntType:POUInt
が、
associatedtype UIntType:POUInt
になったり、enum Bitが廃止予定なのでIntに変更しろ」
性能、20!/19!の計算で、BigIntとの速度比較で500倍近くあった速度差が100倍を余裕で切るところまできました。PONSのもくろみはあくまでProtocol-Orientedな数値型を実現することにあり、
Literal Convertible
Swift 2.
var i = 1
正解はIntです。なぜ? 1はIntですから。では次のdは?
var d = Double(1)
正解はもちろんDouble。1はIntでもDouble()に食わせているのですから、Doubleになるのはごく自然です。では、xは?
var x:Double = 1
やはりDoubleです。print(x)してみると、1.と表示されます。1ではなくて。これはどうでしょう?
var y:Double = "1"
今度はerror: cannot convert value of type 'String' to specified type 'Double'というエラーを出して止まります。しかし、
var z = Double("1")
とすると……、zには無事、Doubleの1.が代入されるではありませんか。これはいったいどういうことなのでしょう?
「DoubleはIntegerLiteralConvertibleプロトコルに準拠しているが、StringLiteralConvertibleプロトコルには標準では準拠していない」
え? 答えになっていない?
では実際にvar d:Double = "1"を受け付けるようにしてみましょう。リスト1のようなコードをvar d:Double = "1"の前にペーストしてみてください。するとあら不思議。今度はエラーにならずにdに42.が代入されています
extension Double:StringLiteralConvertible {
public init(stringLiteral: String) {
self.init(stringLiteral)!
}
public init(unicodeScalarLiteral: String) {
self.init(stringLiteral: "\(unicodeScalarLiteral)")
}
public init(extendedGraphemeClusterLiteral: String) {
self.init(stringLiteral: extendedGraphemeClusterLiteral)
}
}
リスト1中のLiteral Convertibleとは、IntegerLiteralConveribleなら整数リテラルからの、StringLiteralConvertibleなら文字列リテラルからの、
42.0 + "0.195"
はエラーとはならずに、42.になってしまうのです。
便利といえば便利ですが、DoubleはIntegerLiteralConvertibleであっても、StringLiteralConvertibleでないという仕様は、
42 + 0.195
はエラーとならずに42.になる一方、
var i = 42 + 0.195
はエラーとなるわけです。
ちなみに、Literal Convertibleプロトコルが存在します。
ArrayLiteralConvertibleBooleanLiteralConvertibleDictionaryLiteralConvertibleExtendedGraphemeClusterLiteralConvertibleFloatLiteralConvertibleNilLiteralConvertibleIntegerLiteralConvertibleStringLiteralConvertibleStringInterpolationConvertibleUnicodeScalarLiteralConvertible
PONSでは、BigIntとBigUIntはIntegerLiteralConvertibleとString LiteralConvertibleに、BigFloatとRationalはIntegerLiteralConvertibleとFloat LiteralConvertibleにそれぞれ準拠しています。おかげでリスト2のように、
var bq:BigRat = 42.195 // (5938418321153065/140737488355328)
という具合に小数で分数を初期化することも可能になっています。
var bi:BigInt =
"93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000"
ちなみにリスト2のbiですが、import PONSされた状態であれば、
(1...100).reduce(BigInt(1),combine:*)
として手軽に生成できます。もしIntegerLiteral Convertibleでなければ、
(1...100).map{ BigInt($0) }.reduce(BigInt(1),combine:*)
のように、Intを明示的に変換したうえで.reduceしなければならなかったでしょう。
フィボナッチ数を計算する総称関数も、n < 2の2は、IntではなくTなのですが、T(2)と書く必要はないのです。
func fib<T:POInteger>(n:T)->T{
if n < 2 { return n }
var (a, b):(T, T) = (0, 1)
for _ in 2...n {
(a, b) = (b, a + b)
}
return b
}
Swiftの'ミステリー'
リテラルといえば、Stringのリテラルは、""{}で囲まれた部分がブロック=関数リテラル、[]で囲まれた部分が添字またはArray/`
その中にあって異彩を放つのが、'
そうやって見てみると、
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/192ページ
定価1,342円
(本体1,220円+税10%)
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」「使いたい」「発信したい」をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集 - 短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)
