gpt4 book ai didi

带有元素的数组上的 Swift flatMap 是可选的具有不同的行为

转载 作者:可可西里 更新时间:2023-11-01 00:36:32 26 4
gpt4 key购买 nike

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)]

最佳答案

作为@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?。因为 TInt?,这个闭包现在将返回 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
}

故事的寓意

远离双重包装的可选值,它们会给你带来 super 困惑的行为!

如果您正在使用 flatMap,那么如果您传入 [Int?],您应该期望返回 [Int] .如果您想保留元素的可选性,请改用 map

关于带有元素的数组上的 Swift flatMap 是可选的具有不同的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37505787/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com