リテラルと型の話 #__swift__
TRANSCRIPT
EZ-NET 熊⾕友宏 http://ez-net.jp/
2015.12.20 @ 集まれ Swift 好き!Swift 愛好会
リテラルと型の話Swift カジュアルプログラミング
Swift 2.1.1
熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai
CodePiece
iOS, OS X, Apple Watch アプリ
ソースコードを Twitter と Gist に同時投稿できる。
いつもの電卓計算式も見える電卓アプリ。 watchOS 1 対応
音で再配達ゴッド簡単操作で 再配達の申し込み。
EZ-NET IP PhoneiPhone でひかり電話を使う。 自宅 LAN からの利用専用
CodePiece for OS X勉強会を楽しむアプリ
ソースコードを Twitter と Gist に同時投稿できる勉強会で知見をみんなと共有したい時とかに便利!
できること
#__swift__
熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai
横浜 iPhone 開発者勉強会#yidev
勉強会
わいわい・ゆるく、iPhone 開発者のみんなで楽しく過ごすのが目的の会
【 横浜・馬車道 】カジュアル Swift 勉強会
#cswift
ゆるくみんなで Swift を語らえる場を作りたくて始めた会
【 横浜・青葉台 】
第22回を 2016-03-05 に開催予定 第4回を 2015-12-26 に開催
熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai
Xcode 5 徹底解説 MOSA
書籍 / 登壇
Xcode 5 の全機能を徹底的に解説した本
OSX/iOS 系の歴史深い有料会員制の勉強会
紙版は絶版、電子書籍は販売中 秀和システム 10x-Eng.com で取扱予定
Xcode 7 でも役立つはず 法人会員も多数
リテラル
▶ 整数リテラル ▶ 小数点数リテラル ▶ 真偽値リテラル ▶ 文字列リテラル ▶ nil リテラル ▶ 配列リテラル ▶ 辞書リテラル
種類
… 3 … 3.1 … true, false … "STRING" … nil … [ value, ... ] … [ key : value, ... ]
リテラル【 Literal 】 ▶ 書いたまんま、というのを表す言葉 ▶ 高級言語の用語で、コードに直接記載された値
即値【 Immediate 】 ▶ 直接的なものを表す言葉 ▶ アセンブラ用語で、コードに直接埋め込んだデータ
言葉が違うからリテラルを即値と訳すのは誤りとする説も ありますが、訳として間違いとも言えない気もします。
【補足】リテラルと即値
リテラルの書式 (1/7)
▶ 2進数、8進数、10進数、16進数で表記可能 ▶ 各数字の後ろに自由に _ を挿入できる
整数リテラル
200 // 10 進数 0xc8 // 16 進数 0o310 // 8 進数 0b11001000 // 2 進数
20_000_000 // _ は無視される
リテラルの書式 (2/7)
▶ 10進数、16進数で表記可能 ▶ 各数字の後ろに自由に _ を挿入できる
小数点数リテラル
2.10 // 10進数
1.25e-4 // 10進数 … 1.25 × 10-4 0x15.8p3 // 16進数 … 0x15.8 × 23
1.001_001_e-8 // _ は無視される
リテラルの書式 (4/7)
▶ 引用符で括って文字列を表現 ▶ 特別な文字も挿入可能
文字列リテラル
"Swift" // Swift
"\"Swift\"" // "Swift" "Header:\tText" // Header: Text "!\u{20DD}" // ! ⃝
リテラルの書式 (6/7)
▶ 複数の要素を持つことを表現 ▶ 各要素は自由に指定
配列リテラル
[ 1.5, val, "STR" ] // 3つの要素を持つ配列 [] // 何も要素を持たない配列
リテラルの書式 (7/7)
▶ 複数の要素を持つことを表現 ▶ 各要素は キー と 値 の対で自由に指定
辞書リテラル
[ "A": 10, "B": 20 ] // 2つの要素を持つ辞書 [ : ] // 何も要素を持たない辞書
リテラルの大事なところ
▶ リテラルだけでは値はできない
リテラル自体に型はない
let value = nil
Type of expression is ambiguous with more context
リテラルの大事なところ
▶ リテラルが型の値に変換される ▶ 前後関係から適切な型が決定される
型を明記して値にする
let value = nil as Int?
let value:Int? = nil
リテラルの大事なところ
▶ リテラルは型を明示して値にする ▶ 明示されないときは 既定の型 とみなす
リテラルを型に嵌める
// 整数リテラルが Double 型に決まる let value = 10 as Double
// 整数リテラルが Int 型とみなされる let value = 10
Double 型
Int 型
型が明示されないときの既定の型リテラルの大事なところ
▶ 整数リテラル ▶ 小数点数リテラル ▶ 真偽値リテラル ▶ 文字列リテラル ▶ nil リテラル ▶ 配列リテラル ▶ 辞書リテラル
… Int 型 … Double 型 … Bool 型 … String 型 … なし … Array 型 … Dictionary 型
リテラルの大事なところ
▶ 前後関係からも型が推論される ▶ たとえば、代入や計算
前後関係からも型が決まる
// Double 型の変数があるとき let value:Double = 10
// 整数リテラルが Double 型とみなされる let answer = value * 10
Double 型
リテラルの大事なところ
▶ 前後関係からも型が推論される ▶ たとえば、関数の引数
前後関係からも型が決まる
// Double 型を受け取る関数があるとき func doSomething(value:Double) {
}
// 渡したリテラルが Double 型とみなされる doSomething(10)
Double 型
型が既に決められている場合はそれに従う let array: [Int] = [10, 20, 30]
内容から [Double] 型と推論 let array = [3.01, 3.12, 3.23, 3.34]
オブジェクト型は継承関係も考慮して推論 let array = [りんご(), みかん(), ぶどう()]
プロトコルの準拠性や継承関係では推論されない
【余談】配列リテラルからの型推論
[Int] 型
[Double] 型
[くだもの] 型
▶ 型がリテラルの受け入れを表明する ▶ 対応リテラルは LiteralConvertible で決定
// IntegerLiteralConvertible を表明していれば enum MyVariant : IntegerLiteralConvertible {
}
// 整数リテラルを受け入れられる let value: MyVariant = 10
リテラルを受け入れられる型型がリテラルを引き受ける
▶ イニシャライザーでリテラルを受け取る ▶ 受け取った値を適切なデータに変換する
enum MyVariant : IntegerLiteralConvertible {
case IntegerValue(Int) case FloatingPointValue(Double)
init(integerLiteral value: Int) {
self = .IntegerValue(value) } }
型が責任を持って値を作る型がリテラルを引き受ける
受け入れ表明で使うプロトコル型がリテラルを引き受ける
▶ 整数リテラル
▶ 小数点数リテラル
▶ 真偽値リテラル
▶ 文字列リテラル
▶ nil リテラル
▶ 配列リテラル
▶ 辞書リテラル
… IntegerLiteralConvertible
… FloatLiteralConvertible
… BooleanLiteralConvertible
… StringLiteralConvertible
… NilLiteralConvertible
… ArrayLiteralConvertible
… DictionaryLiteralConvertible
定義 protocol IntegerLiteralConvertible {
typealias IntegerLiteralType
init(integerLiteral value: IntegerLiteralType) }
整数リテラルを受け取るときに使うイニシャライザーが 規定されています。これに準拠した型は整数リテラルを 受け入れられる型になります。
【補足】IntegerLiteralConvertible
定義 protocol StringLiteralConvertible : ExtendedGraphemeClusterLiteralConvertible { typealias StringLiteralType init(stringLiteral value: StringLiteralType) }
protocol ExtendedGraphemeClusterLiteralConvertible : UnicodeScalarLiteralConvertible { typealias ExtendedGraphemeClusterLiteralType init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) }
protocol UnicodeScalarLiteralConvertible { typealias UnicodeScalarLiteralType init(unicodeScalarLiteral value: UnicodeScalarLiteralType) }
文字列リテラルを受け取るときに使うイニシャライザーが 規定されています。複数のプロトコルを継承しています。 init(stringLiteral:) 以外の使われ方を教えてください !
【補足】StringLiteralConvertible
▶ 型がリテラルを引き受ける ▶ リテラル自体に型はない
おさらいリテラルと型の関係
// リテラルは、いろんな型に染まる
let value: Int = 10
let value: UInt64 = 10
let value: Double = 10
let value: NSNumber = 10
▶ リテラルをどう扱うかは型の責任 ▶ リテラルの意味を尊重することは重要
// 整数リテラルを 32 bit 整数 として扱う let number: Int32 = 100
// nil リテラルを 何もない として扱う let value: String? = nil
// 配列リテラルを オプションセット として扱う let options: UIViewAutoresizing = [.FlexibleWidth, .FlexibleHeight]
リテラルの扱いは型の裁量で決まるリテラルと型の関係
▶ 型の内容が UInt64 で表現されるとき ▶ リテラルが Int 型では表現しきれない
struct UserID : IntegerLiteralConvertible { var value: UInt64 init(integerLiteral value: Int) { self.value = UInt64(value) } }
リテラルを 64 bit 符号なし整数で扱いたいリテラルから受け取る値
▶ リテラルは任意の型で受け取れる ▶ ただし、互換性のあるものに限る
struct UserID : IntegerLiteralConvertible { var value: UInt64 init(integerLiteral value: UInt64) { self.value = value } }
リテラルを 64 bit 符号なし整数で受け取るリテラルから受け取る値
▶ 小さい型でも受け取れる ▶ 範囲を超えると ビルドタイム でエラー
struct Word : IntegerLiteralConvertible { init(integerLiteral value: UInt8) { … } }
let word1: Word = 255
let word2: Word = 300
リテラルの値の範囲を絞れるリテラル変換で受け取る値
Integer literal '300' overflows when stored into 'UInt8'
定義 struct MyIntegerLiteral : _BuiltinIntegerLiteralConvertible {
var value: Int8
init(_builtinIntegerLiteral value: _MaxBuiltinIntegerType) {
self.value = Int8(_builtinIntegerLiteral: value) } }
使用 struct MyStruct : IntegerLiteralConvertible {
init(integerLiteral value: MyIntegerLiteral) {
} }
このようにするとリテラルの値を自作の型で受け取れます。 ただし、ビルド時のオーバーフロー検出は行われません。
【余談】IntegerLiteralType の互換性
▶ リテラルに型を指定して値を作る ▶ 変換イニシャライザーが呼び出される
enum MyVariant : IntegerLiteralConvertible { }
// A. 型を明記した変数にリテラルを入れる方法 let value: MyVariant = 200
// B. リテラルに型を明記する方法 let value = 200 as MyVariant
リテラルから値を生成するリテラルの動きを捉える
▶ 複数のリテラルコンバーティブルに準拠 ▶ それぞれのリテラルを受容可能になる
// 型が複数のリテラルを受け入れるときに enum MyVariant : IntegerLiteralConvertible, FloatLiteralConvertible { }
// 対応するどのリテラルからも生成可能 let value1: MyVariant = 200 let value2: MyVariant = 150.8
型を複数のリテラルに対応するリテラルの動きを捉える
▶ リテラルと直接的に比較できる ▶ 型が比較可能なことが条件
enum MyVariant : IntegerLiteralConvertible, Equatable { }
let value: MyVariant = 200
// リテラルと直接的に比較可能 if value == 300 {
}
値をリテラルと比較するリテラルの動きを捉える
▶ リテラルを直接引数に渡せる ▶ 目的の型の値へ暗黙的に変換される
// 型を引数で受け取る関数に func calculate(value: MyVariant) { }
// 対応するリテラルを直接的に渡せる calculate(120)
関数の引数にリテラルで渡すリテラルの動きを捉える
▶ リテラル対応の型を Raw 値に使える ▶ 使えるリテラルは整数、小数、文字列のみ
// 列挙子の値はリテラルで指定する enum Coefficient : MyVariant { case Treble = 3 case Halve = 0.5 }
// Raw 値を MyVariant 型で取得できる Coefficient.Treble.rawValue
列挙型のRaw 値で自作の型を使うリテラルの動きを捉える
【補足】列挙型の Raw 値で使えるリテラル
▶ 整数リテラル ▶ 小数点数リテラル ▶ 文字列リテラル
複数種類のリテラルを受け入れられる型を Raw 型に指定すると Raw 値に複数のリテラルを混ぜて指定できるようになります。
import UIKit
let color: UIColor = [#Color(colorLiteralRed: 0.6587, green: 1.0000, blue: 0.6987, alpha: 1.0000)#]
概要カラーリテラル
[#Color(colorLiteralRed: R, green: G, blue: B, alpha: A)#]
protocol _ColorLiteralConvertible {
init(colorLiteralRed: Float, green: Float, blue: Float, alpha: Float) }
Literal Convertibleカラーリテラル
extension NSColor : _ColorLiteralConvertible { }
extension UIColor : _ColorLiteralConvertible { }
Playground で使うカラーリテラル
1. コード行に [#Color#] と書く
2. インラインに 箱 が描かれる
3. 箱をダブルクリックして カラーピッカー で選べる
Playground
import UIKit
let image: UIImage = [#Image(imageLiteral: "Swift.png")#]
概要画像リテラル
[#Image(imageLiteral: PATH)#]
protocol _ImageLiteralConvertible {
init(imageLiteral: String) }
Literal Convertible画像リテラル
extension NSImage : _ImageLiteralConvertible { } extension UIImage : _ImageLiteralConvertible { }
Playground で使う画像リテラル
A. コード行に直接書く [#Image(imageLiteral: "Swift.png")#]
Playground
OR
B. コードに画像を直接ドロップする
import Foundation
let path: NSURL = [#FileReference( fileReferenceLiteral: "main.mm")#]
概要ファイルリテラル
[#FileReference(fileReferenceLiteral: PATH)#]
protocol _FileReferenceLiteralConvertible {
init(fileReferenceLiteral: String) }
Literal Convertibleファイルリテラル
extension NSURL : _FileReferenceLiteralConvertible { }
Playground で使うファイルリテラル
A. コード行に直接書く [#FileReference(fileReferenceLiteral: "main.mm")#]
Playground
OR
B. コードにファイルを直接ドロップする