(注:の廃止など、Swift 3 以降の構文変更を反映するために回答が更新されましたImplicitlyUnwrappedOptional
。)
Optional.map()
そしてOptional.flatMap()
、次のように宣言されます (ここでは無関係な throws/rethrows 修飾子を省略しました):
func map<U>(_ transform: (Wrapped) -> U) -> U?
func flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
「マップ」を使用した最初の例の単純化されたバージョンを考えてみましょう。
let number: Int? = 1
let res1 = number.map { $0 + 1 }
print(res1) // Optional(2)
number
は型Int?
を持ち、クロージャー型は と推測され(Int) -> Int
ます。U
でありInt
、戻り値の型は ですInt?
。number
は ではないnil
ので、アンラップさ1
れてクロージャーに渡されます。クロージャは returnと return2
をmap
返しますOptional(2)
。である場合、結果は になりnumber
ます。nil
nil
次に、「flatMap」を使用した 2 番目の例の簡略版を検討します。
let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)
flatMap
は type のクロージャを想定しています(Wrapped) -> U?
が{ $0 + 1 }
、optional を返しません。コンパイルするために、コンパイラはこれを次のように変換します。
let res2 = number.flatMap { return Optional($0 + 1) }
これで、クロージャの typeが(Int) -> Int?
になり、再びです。再びラップが解除され、クロージャに渡されます。クロージャーは、 からの戻り値でもある戻り値を返します。だった場合、またはクロージャーが返された場合、結果は になります。U
Int
number
Optional(2)
flatMap
number
nil
nil
nil
したがって、実際にはこれらの呼び出しに違いはありません。
let res1 = number.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }
しかし、それflatMap
は意図されたものではありません。より現実的な例は
func foo(_ s : String?) -> Int? {
return s.flatMap { Int($0) }
}
print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int($0)` returns nil)
print(foo(nil)) // nil (because the argument is nil)
一般に、型と変換map
のクロージャをとります(Wrapped) -> U
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> Optional<U>.some(transform(wrapped))
flatMap
型のクロージャを取り、(Wrapped) -> U?
変換します
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)
ここtransform(wrapped)
もそうかもしれませんOptional<U>.none
。
(あなたの例のように)オプションを返さないflatMap
クロージャで呼び出された場合、コンパイラはそれをオプションに自動的に変換し、もう違いはありません。map