let arr: [Int?] = [1,2,3,4,nil]
let arr1 = arr.flatMap { next in
next
}
// arr1: [1,2,3,4]
let arr2: [Int?] = arr.flatMap { next -> Int? in
next
}
// arr2: [Optional(1), Optional(2), Optional(3), Optional(4)]
我对这些代码感到困惑,他们为什么会有所作为?
更新:请参阅这些代码,我
let arr: [Int?] = [1,2,3,4,nil]
let arr1: [Int?] = arr.flatMap { next in
next
}
// arr1: [Optional(1), Optional(2), Optional(3), Optional(4), nil]
let arr2: [Int?] = arr.flatMap { next -> Int? in
next
}
// arr2: [Optional(1), Optional(2), Optional(3), Optional(4)]
答案 0 :(得分:4)
作为@Adam says,由于您为结果提供的显式类型。在第二个例子中,这会导致双重包装选项引起的混乱。为了更好地理解这个问题,我们来看看flatMap
函数签名。
@warn_unused_result
public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
当您明确指定结果属于[Int?]
类型时,因为flatMap
返回通用类型[T]
- Swift会将T
推断为Int?
现在这会引起混淆,因为传递给flatMap
的闭包需要一个元素输入并返回T?
。因为T
是Int?
,所以此闭包现在将返回T??
(双包装可选)。这样可以很好地编译,因为类型可以自由地提升为选项,包括提升为双选项的选项。
所发生的事情是,数组中的Int?
个元素被提升为Int??
个元素,然后flatMap
将它们展开回Int?
}。这意味着nil
元素无法从arr1
过滤掉,因为它们会被双重包裹,而flatMap
仅在第二层包装上运行。
为什么arr2
能够将nil
过滤掉,似乎是因为关闭的促销传递给flatMap
。因为您明确地将闭包的返回类型注释为Int?
,所以闭包将从(Element) -> Int?
隐式提升为(Element) -> Int??
(闭包返回类型可以像其他方式一样自由地提升类型) - 而不是元素本身从Int?
升级到Int??
,因为没有类型注释,闭包将被推断为(Element) -> Int??
。
这个怪癖似乎允许nil
避免被双重包裹,因此允许flatMap
过滤掉它(不完全确定这是否是预期的行为)。
您可以在以下示例中看到此行为:
func optionalIntArrayWithElement(closure: () -> Int??) -> [Int?] {
let c = closure() // of type Int??
if let c = c { // of type Int?
return [c]
} else {
return []
}
}
// another quirk: if you don't explicitly define the type of the optional (i.e write 'nil'),
// then nil won't get double wrapped in either circumstance
let elementA : () -> Int? = {Optional<Int>.None} // () -> Int?
let elementB : () -> Int?? = {Optional<Int>.None} // () -> Int??
// (1) nil gets picked up by the if let, as the closure gets implicitly upcast from () -> Int? to () -> Int??
let arr = optionalIntArrayWithElement(elementA)
// (2) nil doesn't get picked up by the if let as the element itself gets promoted to a double wrapped optional
let arr2 = optionalIntArrayWithElement(elementB)
if arr.isEmpty {
print("nil was filtered out of arr") // this prints
}
if arr2.isEmpty {
print("nil was filtered out of arr2") // this doesn't print
}
避开双重包裹的选项,他们可以给你超级混乱的行为!
如果您正在使用flatMap
,那么如果您通过[Int]
,则应该会期望返回[Int?]
。如果您想保留元素的可选性,请改用map
。
答案 1 :(得分:1)
它与您提供的显式类型arr2
有关。当您指定arr2
必须是[Int?]
类型时,Swift符合并简单地将值包装在可选项中。但是,flatMap
操作返回数组的非零值,这使arr1
为[Int]
类型的原因显而易见。要查看flatMap
返回数组的非零值,只需查看函数的声明注释即可。此外,如果您了解flatMap
操作通常使用的内容,即它解开值的内部包装,您可以看到函数返回类型[Int]
的值的原因。考虑arr
,其类型为[Int?]
。在其包装形式中重写arr
的类型,它变为Array<Optional<Int>>
。对于要打开内包装的flatMap
函数,函数必须返回类型Array<Int>
的值。为此,函数必须抛出零值。