mapを学ぶ

さて、少しずつSwiftにも触れていきます。

巷では「Swiftなら関数型プログラミングだよ」「map/flatMapはSwiftのたしなみ」のようなことが言われていて、実際のところ有名どころのライブラリは、こうした考え方を活用して作られています。ですが、正直なところ私はこれらを理解しているとはいえない状況なので、まずはこのmap,flatMap辺りから学んでいこうと思います。

map()

オンラインマニュアルのmap()に関する記述を調べてみると、いくつかの型でメソッドとして用意されていて、それぞれ次のように書かれています。

Optional, ImplicitlyUnwrappedOptional

/* If self == nil, returns nil. Otherwise, returns f(self!). */
public func map<U>(@noescape f: (T) -> U) -> U? // Optional
public func map<U>(@noescape f: (T) -> U) -> U! // ImplicitlyUnwrappedOptional

説明にある「もしself == nilの場合はnilを返し、そうでなければf(self!)の結果を返す」ということは、たとえばOptional Bindingを使って書く以下の処理:

let transformed: String?
if let foo = optionalfoo {
    transformed = String(foo)
} else {
    transformed = nil
}

は、map()を使えば次のようにもっとシンプルに書けるということですね(極端な例ですが…)。ふむふむ。

let transformed = optionalfoo.map{ String($0) }

あ、書いていて気づいたのですが、このtransformedの型はOptional型のままで、処理の前後で変数のOptional性(とでもいうのかな)は変化しないです。定義にあるようにOptionalのmap()はU?を返し、ImplicitlyUnwrappedOptionalのmapもU!を返しています。nilの場合はnilを返すので当然なのですが、ImplicitlyUnwrappedOptionalもそのまま同じで扱えるようになっているのは面白いです。

CollectionType, SequenceType

次はコレクション/シーケンス型です。コレクションに対してTの配列を返します。

swift public func map<T>(@noescape transform: (Self.Generator.Element) -> T) -> [T]

Return an Array containing the results of mapping transform over self.

transform引数がT単体を返すことを求めていることから、結果の要素数は元のコレクションの要素数と同じになるようです。本当にそうかなと試してみると、

(0...3).map{ i -> [String] in
    [String(i)]
} // [["0"], ["1"], ["2"], ["3"]]

(0...3).map{ i -> String? in
    if i%2 == 0 {
        return String(i)
    } else {
        return nil
    }
} // [{Some "0"}, nil, {Some "2"}, nil]

ということで、やはり同じ要素数になります。

LazyBidirectionalCollection, LazyForwardCollection, LazyRandomAccessCollection, LazySequence

mapメソッドを持っている型の最後は、「Lazy〜」と名付けられた遅延評価型のコレクションです。

/* Return a MapCollection over this LazyBidirectionalCollection / LazyForwardCollection
   / LazyRandomAccessCollection / LazySequence.
   The elements of the result are computed lazily, each time they are read, by calling
   transform function on a base element. */
public func map<U>(transform: (Base.Generator. Element) -> U) -> XXX <MapCollection <Base, U>>

基本は先ほどのコレクション/シーケンス型と同じですが、こちらは曰く「この(Lazyなコレクション/シーケンス型)を通じてMapCollection型を返す。結果の各要素は、実際に読まれる段階になってはじめて変換関数が呼ばれ、計算される」とあります。遅延評価型のコレクション/シーケンス型の1つ1つに対して処理を行うものの、実際に評価されるのは結果の配列にアクセスした時になるということですか。なるほど。