リテラルと型の話 #__swift__

89
EZ-NET 熊⾕友宏 http://ez-net.jp/ 2015.12.20 @ 集まれ Swift 好き!Swift 愛好会 リテラルと型の話 Swift カジュアルプログラミング Swift 2.1.1

Upload: tomohiro-kumagai

Post on 16-Apr-2017

2.205 views

Category:

Software


0 download

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 でも役立つはず 法人会員も多数

混乱しがちな?

リテラルの話

リテラルの話

1. リテラルの基礎を知り 2. リテラルの仕組みを把握して 3. リテラルの実際の動きを掌握する

構成

リテラル概要

リテラル

▶ コードで値を書くのに使う ▶ 具体的な内容を記述する

概要

"Swift"

2.1

リテラル

▶ 整数リテラル ▶ 小数点数リテラル ▶ 真偽値リテラル ▶ 文字列リテラル ▶ 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 // _ は無視される

リテラルの書式 (3/7)

▶ 真か偽かの2種類だけ

真偽値リテラル

true // 真を表現 false // 偽を表現

リテラルの書式 (4/7)

▶ 引用符で括って文字列を表現 ▶ 特別な文字も挿入可能

文字列リテラル

"Swift" // Swift

"\"Swift\"" // "Swift" "Header:\tText" // Header: Text "!\u{20DD}" // ! ⃝

リテラルの書式 (5/7)

▶ 無 を表現

nil リテラル

nil // 無

リテラルの書式 (6/7)

▶ 複数の要素を持つことを表現 ▶ 各要素は自由に指定

配列リテラル

[ 1.5, val, "STR" ] // 3つの要素を持つ配列 [] // 何も要素を持たない配列

リテラルの書式 (7/7)

▶ 複数の要素を持つことを表現 ▶ 各要素は キー と 値 の対で自由に指定

辞書リテラル

[ "A": 10, "B": 20 ] // 2つの要素を持つ辞書 [ : ] // 何も要素を持たない辞書

リテラルを使う

リテラルの使い方

▶ 変数に入れて使う

コード内にリテラルを書く

let name = "Swift"

let version = 2.1

リテラルの大事なところ基礎

リテラルに 型 はない

リテラルの大事なところ

▶ リテラルだけでは値はできない

リテラル自体に型はない

let value = nil

Type of expression is ambiguous with more context

リテラルを型に嵌める必要がある

リテラルの大事なところ

▶ リテラルが型の値に変換される ▶ 前後関係から適切な型が決定される

型を明記して値にする

let value = nil as Int?

let value:Int? = nil

でも、型を明記しないでリテラルを使えていたような?

let name = "Swift"

let version = 2.1

リテラルの既定の型

リテラルの大事なところ

▶ リテラルは型を明示して値にする ▶ 明示されないときは 既定の型 とみなす

リテラルを型に嵌める

// 整数リテラルが 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

関係性から見えてくることリテラルと型の関係

nil は Optional型?Q.

Optional型が nil を空として扱うA.

関係性から読めてくることリテラルと型の関係

true は Bool 型?Q.

Bool 型が true を真として扱うA.NSNumber 型が真と扱っても良いA.

▶ リテラルをどう扱うかは型の責任 ▶ リテラルの意味を尊重することは重要

// 整数リテラルを 32 bit 整数 として扱う let number: Int32 = 100

// nil リテラルを 何もない として扱う let value: String? = nil

// 配列リテラルを オプションセット として扱う let options: UIViewAutoresizing = [.FlexibleWidth, .FlexibleHeight]

リテラルの扱いは型の裁量で決まるリテラルと型の関係

整数リテラルは Int 型?Q.

たとえば …リテラルから受け取る値

型の内容が UInt64 で 表現されるとき

リテラルが Int 型だと 表現しきれない

▶ 型の内容が 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 の互換性

リテラルの動きを捉える演習

リテラルから値を生成する1/5

▶ リテラルに型を指定して値を作る ▶ 変換イニシャライザーが呼び出される

enum MyVariant : IntegerLiteralConvertible { }

// A. 型を明記した変数にリテラルを入れる方法 let value: MyVariant = 200

// B. リテラルに型を明記する方法 let value = 200 as MyVariant

リテラルから値を生成するリテラルの動きを捉える

リテラルから値を生成する

リテラル

型を明示

変換イニシャライザー

リテラルの動きを捉える

型を複数のリテラルに対応する2/5

▶ 複数のリテラルコンバーティブルに準拠 ▶ それぞれのリテラルを受容可能になる

// 型が複数のリテラルを受け入れるときに enum MyVariant : IntegerLiteralConvertible, FloatLiteralConvertible { }

// 対応するどのリテラルからも生成可能 let value1: MyVariant = 200 let value2: MyVariant = 150.8

型を複数のリテラルに対応するリテラルの動きを捉える

型を複数のリテラルに対応するリテラルの動きを捉える

整数 リテラル

型を明示

整数用の変換イニシャライザー

小数点数用の変換イニシャライザー

値をリテラルと比較する3/5

▶ リテラルと直接的に比較できる ▶ 型が比較可能なことが条件

enum MyVariant : IntegerLiteralConvertible, Equatable { }

let value: MyVariant = 200

// リテラルと直接的に比較可能 if value == 300 {

}

値をリテラルと比較するリテラルの動きを捉える

値をリテラルと比較するリテラルの動きを捉える

リテラル==値

同じ型での比較原則

型変換イニシャライザー

型を推論

関数の引数にリテラルで渡す4/5

▶ リテラルを直接引数に渡せる ▶ 目的の型の値へ暗黙的に変換される

// 型を引数で受け取る関数に func calculate(value: MyVariant) { }

// 対応するリテラルを直接的に渡せる calculate(120)

関数の引数にリテラルで渡すリテラルの動きを捉える

関数の引数にリテラルで渡すリテラルの動きを捉える

リテラル

関数(value:型)

型変換イニシャライザー

型を推論

列挙型の Raw 値で自作の型を使う5/5

▶ リテラル対応の型を Raw 値に使える ▶ 使えるリテラルは整数、小数、文字列のみ

// 列挙子の値はリテラルで指定する enum Coefficient : MyVariant { case Treble = 3 case Halve = 0.5 }

// Raw 値を MyVariant 型で取得できる Coefficient.Treble.rawValue

列挙型のRaw 値で自作の型を使うリテラルの動きを捉える

列挙型のRaw 値で自作の型を使うリテラルの動きを捉える

Raw 値

列挙型

Raw 型変換イニシャライザー

列挙子A

Raw 型

リテラル

列挙子B リテラル

rawValue

【補足】列挙型の Raw 値で使えるリテラル

▶ 整数リテラル ▶ 小数点数リテラル ▶ 文字列リテラル

複数種類のリテラルを受け入れられる型を Raw 型に指定すると Raw 値に複数のリテラルを混ぜて指定できるようになります。

隠しリテラル?おまけ

カラーリテラルColor Literal

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

Playground での使用例カラーリテラル

Playground

カラーパレットからの入力も可能カラーリテラル

Playground

カーソル行へのドロップで入力

カラーパレットは Edit メニューから表示カラーリテラル

Playground

画像リテラルImage Literal

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. コードに画像を直接ドロップする

Playground での使用例画像リテラル

Playground

ファイルリテラルFile Reference Literal

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. コードにファイルを直接ドロップする

Playground での使用例ファイルリテラル

Playground

型とリテラルの話以上

まとめリテラルと型の話

1. リテラルを書く・リテラルを使う 2. リテラルの大事なところ ✓ リテラルに型はない ✓ リテラルを型に嵌める ✓ リテラルの既定の型

3. リテラルを型に嵌める仕組み ✓ 型がリテラルを受け入れる ✓ リテラルと型の関係

4. リテラルの動きを捉える 5. 隠しリテラル?