flatMap on Optional类型抱怨打开内部封闭

时间:2018-03-26 03:44:35

标签: swift functional-programming

如果它位于顶层,我可以按预期调用flatMap

func id<T>(v: T) -> T {
    return v
}


var foo: Int?
foo.flatMap(id) // returns nil

foo = 1
foo.flatMap(id) // returns 1

但是,当我尝试使用flatMap来实现我只能想象它的强烈目的(在这种情况下组成多个Optionals)时,编译器会抛出错误:

var bar: Int?
foo.flatMap { $0 + bar.flatMap(id) }
// error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
// foo.flatMap { $0 + bar.flatMap(id) }
//                        ^

为什么我在前两个示例中没有打开可选内部的可选项?这有点挫败了flatMap的目的吗?

4 个答案:

答案 0 :(得分:2)

的原因

您无法添加可选项和数字

var v1 : Int?

let sum = v1 + 2 //Will throw a compilation error

这是一个编译错误,因为v1可能是零。将{2}添加到nil

是没有意义的

在您的示例foo.flatMap { $0 + bar.flatMap(id) }

bar.flatMap(id)会返回一个可选的

解决方案

foo.flatMap { $0 + (bar.flatMap(id) ?? 0)}

答案 1 :(得分:2)

您似乎认为flatMap返回未包装的值。它没有。是的,它确实尝试解包原始值,如果此解包成功则调用closure(并返回闭包返回的任何内容),但如果原始值无法解包则返回nil。正如the documentation所说:

  

当此Optional实例不是nil时,评估给定的闭包,将未包装的值作为参数传递。

     

...

     

返回:给定闭包的结果。如果此实例为nil,则返回nil

正如您所看到的,flatMap的结果因此是可选的,例如如果你看一下flatMap声明:

 func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

所以,让我们回过头看看你的第一个代码片段。你说:

var foo: Int?
foo = 1
foo.flatMap(id) // returns 1

没有。它返回Int?,即此示例中的Optional(1)。它不会返回1

那么请考虑一下:

var bar: Int?
let result = foo.flatMap { $0 + bar.flatMap(id) }

由于bar.flatMap(id)将返回Int?,编译器会抱怨您正在尝试添加$0foo的展开值,假设它可以解包它)使用Optional<Int>,导致错误,因为它询问您是否打算从此表达式的后半部分解包该值。

为了说清楚,请考虑这个例子:

let result = foo.flatMap { value -> Int? in
    // calculate some result using `value`, which is `foo` unwrapped,
    // and return it; if `foo` couldn't be unwrapped, this closure is
    // not called and `result` is `nil`

    return calculatedValue
}

这是一个很好的功能,相当于以下内容:

let result: Int?

if let value = foo {
    // calculate result from `value`, which is `foo` unwrapped
    // and update `result` accordingly

    result = calculatedValue
} else {
    // otherwise just set `result` to `nil`

    result = nil
}

答案 2 :(得分:1)

map()不同,flatMap()确实剥离了可选性,但其主要目的是为了展平&#34;展平&#34;一个多维数组。

使用闭包将一个数组中的项映射到另一个数组中,然后加入结果。选项和数组都是&#34;容器&#34;并且flatMap()删除了一级遏制。

在您的示例中,您尝试将一个未包装的可选项添加到可选项中,但这不起作用。

如果要对可选整数数组求和,可以尝试这个例子。

let foo: Int?
let bar: Int?

let myArray = [foo, bar]

let sum = myArray.flatMap().reduce(0, +)

这将返回整数的总和,如果它们都是零,则返回零。

答案 3 :(得分:0)

foo.flatMap(id) 不返回 1。它返回 Optional(1)

foo.flatMap { $0 + bar.flatMap(id) } 中断,因为 foo.flatMap 可以被解包,因此它执行闭包。但是 bar.flatMap(id) 不能被评估为非可选(见我的第一点)。

flatMap 在此上下文中与此代码的作用相同:

var optionalInt = Int("5A")

if let unwrappedInt = optionalInt {
    optionalInt = unwrappedInt + 5
}

注意我们必须如何使用 if let 来解包才能使用 + 运算符?

flatMap 的一个用途是它为我们提供了便利,无需解开即可使用 +。如果表达式评估为 nil,则不会执行。但整个事情返回一个可选的。像这样:

let result = optionalInt.flatMap { 5 + $0 }

此处 result 的计算结果为 nil