詳解Swift(12)〜クロージャ

2016. 11. 04
この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における差異を記述しています。



CHAPTER 12 クロージャ



●12.1 クロージャの宣言


・クロージャの仮引数と返り値の型宣言

定数 c5 の例において、本書では単一文での return 文は返り値の型推論は行われないのでエラーが出るとありますが、現在エラーは出ません。

let c5 = { return 10.0 / 2.5 }        // エラーなし

これに関しては「Xcode 6 Release Notes(Xcode 6.3 Release Notes/Changes, Enhancements, and Notes/Swift Language Enhancements)」に、Swift 1.2 での機能強化で単一式のクロージャの型推論が改善されたと記述があります。

「単一式のクロージャにおける型推論は、いくつかの点で改善されました。

・単一の return 文から成るクロージャは、現在は単一式のクロージャとして型チェックされています。

・非 Void の返り値の型を持つアノテーションの無い単一式のクロージャは、現在は Void コンテキストで使用することができます。

・戻り値の型のアノテーションが見つからないため複数文から成るクロージャの型が推論できなかった状況が、現在は適切に診断されます。」



・クロージャと関数の型

クロージャ式を変数に代入する例で、本書では変数の型を調べるためターミナル上でクロージャの定義を対話的に実行していますが、プレイグラウンドでは dynamicType で型を知ることができます。

let c1 = { (a:Int, b:Int) -> Double in
    return Double(a) / Double(b) }
c1.dynamicType        // ((Int, Int) -> Double).Type


・クロージャの複雑な型宣言

クロージャに対してオプショナル型の変数を定義する例は本書の通りです。

var cp1: (Int, Int) -> Double!
cp1.dynamicType        // ((Int, Int) -> ImplicitlyUnwrappedOptional<Double>).Type

var cp2: ((Int, Int) -> Double)!        // nil
cp2.dynamicType        // ImplicitlyUnwrappedOptional<(Int, Int) -> Double>.Type

クロージャのインスタンスを要素とする空配列の定義では () で優先順位を付けてもエラーになります。

var ca3 = [(Int, Int) -> Double]()        // エラー
var ca3 = [((Int, Int) -> Double)]()        // エラー

typealias MyClosure = (Int, Int) -> Double
var ca3 = [MyClosure]()
ca3.dynamicType        // Array<(Int, Int) -> Double>.Type

また簡略記法ではエラーが出るものでも、パラメータ付き型指定による本来の記法なら問題が解消されます。
(暗黙的開示オプショナル型の例では別のエラーが出ますが…)

var ca4 = (() -> ())!        // エラー

var ca4 = ImplicitlyUnwrappedOptional<() -> ()>()        // 'init()' is deprecated; Parameterless initializer will be removed in Swift 3. Use nil literal instead.
ca4.dynamicType        // ImplicitlyUnwrappedOptional<() -> ()>.Type


var ca5 = [() -> ()]        // エラー

var ca5 = Array<() -> ()>()
ca5.dynamicType        // Array<() -> ()>.Type



●12.2 変数のキャプチャ


・クロージャが参照型の変数をキャプチャする場合

List12-3 において、関数 makerZ のローカル変数 localvar はクラス MyInt のインスタンスであり、更新されないので定数にしたらどうかと警告が出ますので定数にしましょう。

またクロージャ内で localvar.value を前置インクリメントしていますが、インクリメント/デクリメント演算子は Swift 3.0 で削除するため廃止になったので修正します。

func makerZ(a:MyInt, s:String) -> () -> () {
    var localvar = a        // Variable 'localvar' was never mutated; consider changing to 'let' constant
    return { print("\(s): \(++localvar.value)") }        // '++' is deprecated; it will be removed in Swift 3
}


func makerZ(a:MyInt, s:String) -> () -> () {
    let localvar = a
    return { localvar.value += 1; print("\(s): \(localvar.value)") }
}


・メソッドとクロージャ

本書では列挙型や構造体のインスタンスメソッドはクラスのように扱うことができないと記述されていますが、列挙型や構造体のインスタンスメソッドでもクロージャのように扱えキャプチャすることができます。

// 列挙型

enum Direction : String {
    case Up = "上", Right="右", Down="下", Left="左"
    func drcString(drc:String) {
        print("\(drc):\(self.rawValue)")
    }
}

var d:Direction! = Direction.Down
var clo_0:((String)->())! = d.drcString
d = nil
clo_0("向き")        // 向き:下


// 構造体

struct Person {
    let name: String
    init(_ s:String) { name = s }
    func greet(msg:String) {
        print("\(msg), \(name)さん")
    }
}

var p: Person! = Person("朝子")
var clo_1:((String)->())! = p.greet
p = nil
clo_1("おはようございます")        // おはようございます, 朝子さん

ただし参照型のクラスと違い列挙型や構造体は値型なので、参照の循環などを考慮する必要はありません。



●12.3 クロージャの使い方と記法


・配列の整列

配列の要素を並べ替えて新しい配列を作るメソッド sorted は現在エラーが出ます。

let slist = list.sorted( { (a:String, b:String) -> Bool in a < b } )        // error: 'sorted' is unavailable: call the 'sort()' method on the array

これに関しては「詳解Swift(5)〜基本的なデータ型」の「5.3 配列/配列のメソッド」で解説したように、Xcode 7.0 でsorted() が sort() に改名されたためです。

let slist = list.sort( { (a:String, b:String) -> Bool in a < b } )        // ["OLD", "fig.pdf", "filelist1.swift", "sample.swift"]

また演算子 > の場合、記述の仕方によってエラーが出るとありますが現在はエラーが出ません。
(ちなみに「詳解 Swift 改訂版」ではこの件に触れられていません。)

let slist0 = list.sort( { $0 > $1 } )
print("slist0:\(slist0)")        // slist0:["sample.swift", "filelist1.swift", "fig.pdf", "OLD"]

let slist1 = list.sort( { (a:String, b:String) -> Bool in a > b } )
print("slist1:\(slist1)")        // slist1:["sample.swift", "filelist1.swift", "fig.pdf", "OLD"]

let slist2 = list.sort( > )
print("slist2:\(slist2)")        // slist2:["sample.swift", "filelist1.swift", "fig.pdf", "OLD"]

let slist3 = list.sort( { return $0 > $1 } )
print("slist3:\(slist3)")        // slist3:["sample.swift", "filelist1.swift", "fig.pdf", "OLD"]


・配列要素の選択

List12-6 の実行例で、フィルタの for-in 文でエラーが出ます。

for ch in $0 {        // error: type 'String' does not conform to protocol 'SequenceType'

これに関しては「詳解Swift(1)〜Swiftでプログラミング」の「1.2 制御構文/for-in文」で解説したように、Xcode 7.0 で String が SequenceType に準拠しなくなったためです。

したがって、文字列から文字を取り出すには .characters プロパティを使用する必要があります。

for ch in $0.characters {


・配列要素に対する操作

配列 dat から10以上のデータを昇順に並べて文字列に変換する例において、上記の「配列の整列」で述べたように sorted は sort に修正する必要があります。

let str_ = dat.filter{ $0 >= 10 }.sort( < ).map{ "\($0)" }        // ["13", "45", "80"]


また1から10までの和を求める例の2つ目において、reduce は関数なので「詳解Swift(2)〜関数」の「外部引数名」の項で述べたように第2引数以降は外部引数名を指定する必要があります。

let sum_ = numbers.reduce(0, +)        // error: missing argument label 'combine:' in call


let sum_ = numbers.reduce(0, combine: +)        // 55


・関数のカリー化と部分適用

関数 timesCurried などのカリー化関数構文は警告が出るようになりました。

func timesCurried_(a: Double)(b: Double) -> Double {        // Curried function declaration syntax will be removed in a future version of Swift; use a single parameter list

これに関しては「Xcode 7 Release Notes(Xcode 7.3 Release Notes/New Features/Swift)」に、Swift 3 でカリー化関数構文は廃止するためとの記述があります。

「カリー化関数構文は Swift 3 で削除される予定なので廃止されました。(23364870)」

したがってカリー化を止めるか専用構文を使用しない記述に修正する必要があります。
(「Qiita/Swift 初心者がまとめる Xcode 7.3 で Warning になった Swift の文法」、「Qiita/Swift 2.2 で変わったところ」参照)

func timesCurried_(a: Double, b: Double) -> Double {        // Fix-it   Replace ")(" with ","



●12.4 クロージャと強い参照の循環


・キャプチャによる強い参照

List12-7 において、関数 meter のクロージャ式で引数 distance を変数に指定していますが、Swift 3 で廃止になるとのエラーが出るので修正する必要があります。

return { (var distance:Int) in        // 'var' parameters are deprecated and will be removed in Swift 3


return { (dis:Int) in
        var distance = dis

また実行例において、「詳解Swift(1)〜Swiftでプログラミング」で述べたように do-while 文は repeat-while に名称変更、C 言語様式の for 文は非推奨になったため stride 型を使った for-in 文に修正する必要があります。

do {        // 'do-while' statement is not allowed; use 'repeat-while' instead
    (中略)
    for var d = 1000; d <= 5000; d += 1000 {        // C-style for statement is deprecated and will be removed in a future version of Swift
    (中略)
}while false


repeat {
    (中略)
    for var d in 1000.stride(through: 5000, by: 1000) {
    (中略)
}while false



Qiita/Swift 初心者がまとめる Xcode 7.3 で Warning になった Swift の文法

Qiita/Swift 2.2 で変わったところ

詳解 Swift 改訂版(Amazon)
 






Bose SoundLink around-ear wireless headphones II
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
06 | 2017/07 | 08
Sun Mon Tue Wed Thu Fri Sat
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 - - - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

Bose SoundLink around-ear wireless headphones II
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR