97
My Sweet Swi

20141128 iOSチーム勉強会 My Sweet Swift

  • Upload
    necocen

  • View
    750

  • Download
    2

Embed Size (px)

Citation preview

Page 1: 20141128 iOSチーム勉強会 My Sweet Swift

My Sweet Swift

Page 2: 20141128 iOSチーム勉強会 My Sweet Swift

Agenda

• Generics

• Enumeration

• Optional

• Appendix

Page 3: 20141128 iOSチーム勉強会 My Sweet Swift

Agenda

• Generics

• Enumeration

• Optional

• Appendix

}準備本題

Page 4: 20141128 iOSチーム勉強会 My Sweet Swift

Generics

Page 5: 20141128 iOSチーム勉強会 My Sweet Swift

Generics• 型をパラメータにとる関数・データ構造

• 型情報を保ったまま、複数の型に同時に対応できる

• とは?

Page 6: 20141128 iOSチーム勉強会 My Sweet Swift

例: 入力をそのまま返す関数

func identity(input: Int) -> Int { return input } !let number = identity(42) // Int let string = identity("Lorem ipusm") // error

Page 7: 20141128 iOSチーム勉強会 My Sweet Swift

例: 入力をそのまま返す関数

func identity(input: Any) -> Any { return input } !let number = identity(42) // Any let string = identity("Lorem ipsum") // Any !number * 2 // error string + string // error

Page 8: 20141128 iOSチーム勉強会 My Sweet Swift

例: 入力をそのまま返す関数

func identity<T>(input: T) -> T { return input } !let number = identity(42) // Int let string = identity("Lorem ipsum") // String !number * 2 string + string

Page 9: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

x

"\(x)"

ffunc f(x: Int) -> String { return "\(x)" }

Page 10: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

x [1, 2, 3]

"\(x)"

f

Page 11: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

x [1, 2, 3]

["1", "2", "3"]"\(x)"

f

Page 12: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

x [1, 2, 3]

["1", "2", "3"]"\(x)"

f

map([Int], Int -> String) -> [String]

Page 13: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

func f(x: Int) -> String { return "\(x)" } !func map(xs: [Int], f: Int -> String) -> [String] { var ys = [String]() for x in xs { ys.append(f(x)) } return ys } !map([1, 2, 3], f) // ["1", "2", “3"] map([1, 2, 3], { "\($0)" }) // ["1", "2", "3"]

Page 14: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

Int [Int]

[String]String

f

map([Int], Int -> String) -> [String]

Page 15: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

T [T]

[U]U

T -> U

map([T], T -> U) -> [U]

Page 16: 20141128 iOSチーム勉強会 My Sweet Swift

例: map

func map<T, U>(xs: [T], f: T -> U) -> [U] { var ys = [U]() for x in xs { ys.append(f(x)) } return ys } !// [Int] -> [String] map([1, 2, 3], { "\($0)" }) // ["1", "2", "3"] // [Int] -> [Double] map([1, 2, 3], { $0 * 2.0 }) // [2.0, 4.0, 6.0] // [Double] -> [CGRect] map([1, 2, 3], { CGRect(x: $0, y: $0, width: 100.0, height: 100.0) })

Page 17: 20141128 iOSチーム勉強会 My Sweet Swift

クラスの例: Stack

class Stack<T> { private var array = [T]() func push(item: T) { array.append(item) } func pop() -> T { return array.removeLast() } } !let stack = Stack<Int>() stack.push(1) stack.push(2) stack.push(3) stack.pop() // 3 stack.pop() // 2 stack.pop() // 1

Page 18: 20141128 iOSチーム勉強会 My Sweet Swift

Enumerations

Page 19: 20141128 iOSチーム勉強会 My Sweet Swift

Enums

enum BloodType { case A, B, O, AB } !struct Person { var bloodType: BloodType } !var p = Person(bloodType: BloodType.A) var q = Person(bloodType: .A) q.bloodType = .AB

Page 20: 20141128 iOSチーム勉強会 My Sweet Swift

Enums

• ObjCのEnumと違って、「値」を持たない

• ex. 東西南北に整数を割り当てる意味?

• 従来通りに値を持たせることもできる

• 文字列もOK

Page 21: 20141128 iOSチーム勉強会 My Sweet Swift

…with associated values

/// ダイアログのUI要素を指定するenum。 enum DialogItem { case TextButton(title: String, identifier: String) case GrayButton(title: String, identifier: String) case Text(String) case Title(String) case Margin(CGFloat) case Group([DialogItem]) } !let item1 = DialogItem.Margin(20) let item2 = DialogItem.Text("String")

Page 22: 20141128 iOSチーム勉強会 My Sweet Swift

Associated Values

• enumの各状態に値を紐づけることができる

• 型が違っていてもいい

• 単なる列挙というよりは、「複数の型のどれか」を表す型になる

Page 23: 20141128 iOSチーム勉強会 My Sweet Swift

Switchで値を展開

switch item { case let .Title(title): println("Title: \(title)") case let .Text(text): println("Text: \(text)") case let .TextButton(title: title, identifier: identifier): /// case let .GrayButton(title: title, identifier: identifier): /// case let .Group(subitems): /// default: break }

Page 24: 20141128 iOSチーム勉強会 My Sweet Swift

例: DialogViewController

let items: [DialogViewController.DialogItem] = [ .Title("ダイアログ"), .Text("表示要素を配列で渡すだけで、自在にダイアログを作ることができます。"), .TextButton(title: "ボタン", identifier: "button1"), .Margin(60), .Group([ .Group([.Text("二段組み"), .Text("にも")]), .Group([.Text("対応"), .Text("(たぶん)")]) ]), .Group([ .TextButton(title: "ボタン", identifier: "button2"), .GrayButton(title: "ボタン", identifier: "button3"), .TextButton(title: "ボタン", identifier: "button4") ]) ] let dialog = DialogViewController(items: items, callback: nil) presentViewController(dialog, animated: true, completion: nil)

Page 25: 20141128 iOSチーム勉強会 My Sweet Swift

簡単な例: Failable

• 「失敗するかもしれない」結果を記述する

• 失敗すればFailed、成功すればSucceeded

• 成功した場合は結果をAssociated Valueに持つ

Page 26: 20141128 iOSチーム勉強会 My Sweet Swift

例: Failable<T>

enum Failable<T> { case Succeeded(T) // 成功したときの値 case Failed // 失敗フラグ init(_ value: T) { self = .Succeeded(value) } }

Page 27: 20141128 iOSチーム勉強会 My Sweet Swift

例: Failable<T>

// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Failable<Int> { if x % 2 == 0 { return .Succeeded(x / 2) } else { return .Failed } } !switch failableHalve(12) { case let .Succeeded(r): println("Answer: \(r)") default: println("Odd Number") }

Page 28: 20141128 iOSチーム勉強会 My Sweet Swift

それって

// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Optional<Int> { if x % 2 == 0 { return .Some(x / 2) } else { return .None } } !switch failableHalve(12) { case let .Some(r): println("Answer: \(r)") default: println("Odd Number") }

Page 29: 20141128 iOSチーム勉強会 My Sweet Swift

Optionalなのでは?

// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Int? { if x % 2 == 0 { return x / 2 } else { return nil } } !!if let r = failableHalve(12) { println("Answer: \(r)") } else { println("Odd Number") }

Page 30: 20141128 iOSチーム勉強会 My Sweet Swift

Optional

Page 31: 20141128 iOSチーム勉強会 My Sweet Swift

太古よりnullは人々を苦しめてきた

Page 32: 20141128 iOSチーム勉強会 My Sweet Swift

Optional以前• オブジェクトとはポインタである

• 特別なアドレス(多くの場合0)をNULLと呼び、そこを指すポインタを「無効なオブジェクト」として扱う

Page 33: 20141128 iOSチーム勉強会 My Sweet Swift

問題点• NULLを無効な値だと知っているのは実行中のプログラムとプログラマだけ

• コンパイラには他のポインタ値と区別できない

• →実行時に落ちる

• 予期せぬNULLが紛れ込まないように「気をつける」「確認する」ことしかできなかった

Page 34: 20141128 iOSチーム勉強会 My Sweet Swift

Optionalのいいところ• 「無効な値」という概念を型にしたことで、意味論ではなく構文論として扱える

• 無効な値を扱う操作が「書けない」文法

Page 35: 20141128 iOSチーム勉強会 My Sweet Swift

Optional<T>enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) ! /// Construct a `nil` instance. init() ! /// Construct a non-\ `nil` instance that stores `some`. init(_ some: T) ! /// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`. func map<U>(f: (T) -> U) -> U? ! /// Returns a mirror that reflects `self`. func getMirror() -> MirrorType ! /// Create an instance initialized with `nil`. init(nilLiteral: ()) }

Page 36: 20141128 iOSチーム勉強会 My Sweet Swift

Optional<T>

• 実体はGeneric Enum

• 実はちょっと嘘で、特別扱いを受けている

• cf. http://qiita.com/koher/items/5dd6ce7427e7ecff4249

• (Failable<T>と同じように)箱です

Page 37: 20141128 iOSチーム勉強会 My Sweet Swift

Optional as 箱• 「Tかもしれない」ではなく、「Tの入る箱」

• 値に触るためには箱を開ける必要がある

• binding/coalescing/unwrapping

• 箱を開けずに操作する方法もある

• mapping/chaining

Page 38: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Binding

var optionalInt: Int? !if let int = optionalInt { // int: Int println("optionalInt was \(int)") } else { println("optionalInt was nil") } !var array = [1, 2, 3, 4] while let item = array.last { // item: Int println("\(item)") array.removeLast() }

Page 39: 20141128 iOSチーム勉強会 My Sweet Swift

箱を開ける: Optional Binding

• 値があればtrueに評価されつつ中身を変数に束縛(bind)してくれる

• 一度に一個しかbindできないのは多少不便

Page 40: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Coalescing

optionalInt ?? 3 !// ↓と等価: optionalInt != nil ? optionalInt! : 3 !// あるいは: func coalesce<T>(lhs: T?, rhs: @autoclosure () -> T) -> T { if let lhs = lhs { return lhs } else { return rhs() } } coalesce(optionalInt, 3)

Page 41: 20141128 iOSチーム勉強会 My Sweet Swift

箱を開ける: Optional Coalescing

• 開けられたら開ける

• 開けられなかったら開けない

• かわりにデフォルト値を返す

Page 42: 20141128 iOSチーム勉強会 My Sweet Swift

無理矢理開ける: Forced Unwrapping

• 値があればそれが返るが、なければ落ちる

• 落ちる可能性があるので使うべきではない

• 安全が保証できるなら構わないが……

optionalInt!

Page 43: 20141128 iOSチーム勉強会 My Sweet Swift

箱から出さずに進める方法• Optional mapping

• Optional chaining

Page 44: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Mapping

/// 3倍する関数 func triple(x: Int) -> Int { return x * 3 } !optionalInt = 2 // 以下はすべて.Some 6 optionalInt.map(triple) map(optionalInt, triple) optionalInt.map({ $0 * 3 })

Page 45: 20141128 iOSチーム勉強会 My Sweet Swift

箱を開けない: Optional Mapping

• 箱の中から別の箱の中へ

• 値があればそれを移して別の箱(Optional)を作る

• nilならばnilを返す

Page 46: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Mapping

T

U

T -> U

map(T?, T -> U) -> U?

U?

T?

Page 47: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

class A { var b: B? } !class B { var c: C? } !class C { var i: Int = 1 } var a: A? = A() // a.b.c.iにアクセスしたい

Page 48: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

var a: A? !if let a = a { if let b = a.b { if let c = b.c { println("We finally find \(c.i)!") } } }

Page 49: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

var a: A? !if let i = a?.b?.c?.i { println("Optional chaining makes \(i)t easy.") }

Page 50: 20141128 iOSチーム勉強会 My Sweet Swift

箱を開けない: Optional Chaining

• 失敗するかもしれない呼び出しを連鎖できる

• 失敗すればその先には進まずnil

Page 51: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA?

aはA?型の変数

Page 52: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a.bA?

A?型にbというメンバはない

×

Page 53: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA?

a?.bという形にする

Page 54: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil

aはnilかSomeのどちらか

Page 55: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil

B?

Someならばbに触れる

Page 56: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil nil

B?

nilならそれで終わり

Page 57: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil nil

B?

bはB?型の変数

Page 58: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil nil

B()

or

nil

C?

同じように分岐

Page 59: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

a?.b?.c?.iA()

or

nil nil

B()

or

nil

C()

nil

or

7

以下同文

Page 60: 20141128 iOSチーム勉強会 My Sweet Swift

書き直してみる

extension Optional { func chain<U>(f: T -> U?) -> U? { if let s = self { return f(s) } else { return nil } } } !if let i = a.chain({ $0.b }).chain({ $0.c }).chain({ $0.i }) { println("\(i)") } !// (正確には↓だが説明は省略(結果は等しくなる)) // let k = a.chain({ $0.b.chain({ $0.c.chain({ $0.i }) }) })

Page 61: 20141128 iOSチーム勉強会 My Sweet Swift

なぜ書き直したの?• メンバ関数以外にも使えるようにしたい

• これが今日の主役です

Page 62: 20141128 iOSチーム勉強会 My Sweet Swift

Failable Halve

Int Int?// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Int? { if x % 2 == 0 { return x / 2 } else { return nil } } !if let a = failableHalve(x) { println("Halve(\(x)) = \(a)") } else { println("Failed") }

Page 63: 20141128 iOSチーム勉強会 My Sweet Swift

Failable Halve Again

Int Int?if let a = failableHalve(x) { if let b = failableHalve(a) { println("Halve^2(\(x)) = \(b)") } else { println("Failed") } } else { println("Failed") }

Int Int?

Page 64: 20141128 iOSチーム勉強会 My Sweet Swift

Failable Halve The 3rd

Int Int?if let a = failableHalve(x) { if let b = failableHalve(a) { if let c = failableHalve(b) { println("Halve^3(\(x)) = \(c)") } else { println("Failed") } } else { println("Failed") } } else { println("Failed") }

Int Int? Int Int?

Page 65: 20141128 iOSチーム勉強会 My Sweet Swift

Failable Halve Chaining

Int Int?if let a = failableHalve(x).chain(failableHalve).chain(failableHalve) { println("Halve^3(\(x)) = \(a)") } else { println("Failed") }

Int? Int?

Page 66: 20141128 iOSチーム勉強会 My Sweet Swift

Failable Halve Chaining

Int Int?24 failableHalve(24) // Some 12 failableHalve(24).chain(failableHalve) // Some 6 failableHalve(24).chain(failableHalve).chain(failableHalve) // Some 3 failableHalve(24).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil !36 failableHalve(36) // Some 18 failableHalve(36).chain(failableHalve) // Some 9 failableHalve(36).chain(failableHalve).chain(failableHalve) // nil failableHalve(36).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil

Int? Int?

Page 67: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

t: T

T

Page 68: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

f(t): U?

T U?f

Page 69: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

T U?fU V?g

f(t): U?

Page 70: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

T U?

f(t).chain(g): V?

f V?g

Page 71: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

T U?

f(t).chain(g): V?

f V?g

V h W?

Page 72: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Chaining

T U?

f(t).chain(g).chain(h): W?

f V?g W?h

Page 73: 20141128 iOSチーム勉強会 My Sweet Swift

Appendix

Page 74: 20141128 iOSチーム勉強会 My Sweet Swift

Array Mapping

T [T]

[U]U

T -> U

map([T], T -> U) -> [U]

Page 75: 20141128 iOSチーム勉強会 My Sweet Swift

Optional Mapping

T

U

T -> U

map( T?, T -> U) -> U?

U?

T?

Page 76: 20141128 iOSチーム勉強会 My Sweet Swift

Optional<T>とArray<T>

• T型の値が入った箱

• mapを使えば箱の中身だけを写すことができる

Page 77: 20141128 iOSチーム勉強会 My Sweet Swift

Optional as 状況• ところで、Optionalは「nilかもしれない」という状況(context)を表現していた

• Arrayはどんな状況を表現している?

• →「値が確定していない」

Page 78: 20141128 iOSチーム勉強会 My Sweet Swift

Array as 状況

Kyoto

Page 79: 20141128 iOSチーム勉強会 My Sweet Swift

目がさめると京都にいた

Kyoto

(0, 0)

Page 80: 20141128 iOSチーム勉強会 My Sweet Swift

歩く

Kyoto

(0, 1)

(0,-1)

(1, 0)(-1,0)

Page 81: 20141128 iOSチーム勉強会 My Sweet Swift

歩く

/// 座標 typealias Position = (Int, Int) !/// 東西南北のどこかへ歩きます func walk(from: Position) -> [Position] { let (x, y) = from return [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] } !walk((0, 0)) // [(-1, 0), (1, 0), (0, -1), (0, 1)]

Page 82: 20141128 iOSチーム勉強会 My Sweet Swift

さらに歩く

Kyoto

Page 83: 20141128 iOSチーム勉強会 My Sweet Swift

さらに歩く

Kyoto

Page 84: 20141128 iOSチーム勉強会 My Sweet Swift

Array Chaining

T [U]U

V [W][V]

Page 85: 20141128 iOSチーム勉強会 My Sweet Swift

Array Chaining

extension Array { func chain<U>(f: Element -> [U]) -> [U] { var ys = [U]() for x in self { ys += f(x) } return ys } } !walk((0, 0)).chain(walk).chain(walk)

Page 86: 20141128 iOSチーム勉強会 My Sweet Swift

もっと状況を!• 「値が非同期で返ってくる」も状況

Page 87: 20141128 iOSチーム勉強会 My Sweet Swift
Page 88: 20141128 iOSチーム勉強会 My Sweet Swift

BFTask

doHeavyJobAsync1().continueWithBlock { (task: BFTask!) -> BFTask in let result = task.result as NSString self.resultLabel1.text = result self.resultLabel2.text = "処理中..." return self.doHeavyJobAsync2(result) }.continueWithBlock { (task: BFTask!) -> BFTask in let result = task.result as NSString self.resultLabel2.text = result self.resultLabel3.text = "処理中..." return self.doHeavyJobAsync3(result) }.continueWithBlock { (task: BFTask!) -> AnyObject! in let result = task.result as NSString self.resultLabel3.text = result return nil }

Page 89: 20141128 iOSチーム勉強会 My Sweet Swift

BFTask

private func doHeavyJobAsync2(prevResult: String) -> BFTask { var completionSource = BFTaskCompletionSource() // 5秒待ちの処理 // 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ Util.delay(5, { completionSource.setResult(prevResult + "オワタ") }) return completionSource.task }

Page 90: 20141128 iOSチーム勉強会 My Sweet Swift

Task<T>

class Task<T: AnyObject> { private let task: BFTask ! init(task: BFTask) { self.task = task } var result: T? { return task.result as? T } func chain<U: AnyObject>(f: (T -> Task<U>)) -> Task<U> { let newTask = task.continueWithBlock { f($0.result as T).task } return Task<U>(task: newTask) } }

Page 91: 20141128 iOSチーム勉強会 My Sweet Swift

Taskを返す関数private func doHeavyJobAsync2(prevResult: NSString) -> Task<NSString> { let source = Source<NSString>() // 5秒待ちの処理 // 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ delay(5, { source.setResult(prevResult + "オワタ" as NSString) }) return source.task }

Page 92: 20141128 iOSチーム勉強会 My Sweet Swift

Task Chaining

T UU

V WV

非同期

非同期非同期

Page 93: 20141128 iOSチーム勉強会 My Sweet Swift

Task Chaining

func heavyTask1(Void) -> Task<NSString> { return delayedTask(5) { NSLog("First Task (no params)") return "123" as NSString } } !func heavyTask2(string: NSString) -> Task<NSNumber> { return delayedTask(5) { NSLog("Second Task (param: \(string))") return NSNumber(integer: (string as String).toInt() ?? 0) } } !func heavyTask3(number: NSNumber) -> Task<NSNumber> { return delayedTask(5) { NSLog("Third Task (param: \(number))") return NSNumber(integer: number.integerValue * 2) } }

Page 94: 20141128 iOSチーム勉強会 My Sweet Swift

Task Chaining

NSLog("Start") heavyTask1().chain(heavyTask2).chain(heavyTask3) NSLog("End")

2014-11-27 21:15:24.659 MySweetSwift[18362:314049] Start 2014-11-27 21:15:24.663 MySweetSwift[18362:314049] End 2014-11-27 21:15:29.663 MySweetSwift[18362:314049] First Task (no params) 2014-11-27 21:15:35.136 MySweetSwift[18362:314049] Second Task (param: 123) 2014-11-27 21:15:40.636 MySweetSwift[18362:314049] Third Task (param: 123)

Page 95: 20141128 iOSチーム勉強会 My Sweet Swift

まとめ• Optionalはかわいい!

• ArrayとOptionalは似ている

• すなわち、箱であり、状況である

• 非同期処理も同じように見ることができる

• Optional Chainingのような書きかたができて、それはBFTaskに似ている

Page 96: 20141128 iOSチーム勉強会 My Sweet Swift

参考文献

Page 97: 20141128 iOSチーム勉強会 My Sweet Swift

以上