带有元素的数组上的Swift flatMap是可选的,具有不同的行为 [英] Swift flatMap on array with elements are optional has different behavior

查看:93
本文介绍了带有元素的数组上的Swift flatMap是可选的,具有不同的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我对这些代码感到困惑,为什么它们会有所作为?

I'm confused by these code, why do they make a difference?

更新:请参阅这些代码,我

update: please see these codes, I

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所说,这是由于您要为结果提供的显式类型.在您的第二个示例中,这导致了由双重包装的可选内容引起的混乱.为了更好地理解问题,让我们看一下flatMap函数签名.

As @Adam says, it's due to the explicit type that you're supplying for your result. In your second example, this is leading to confusion caused by double wrapped optionals. To better understand the problem, let's take a look at the flatMap function signature.

@warn_unused_result
public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]

当您明确指定结果的类型为[Int?]时,因为flatMap返回通用类型[T] – Swift将推断TInt?.

When you explicitly specify that the result is of type [Int?], because flatMap returns the generic type [T] – Swift will infer T to be Int?.

现在,这会引起混乱,因为对flatMap的传递闭包采用元素输入并返回T?.因为TInt?,所以此闭包现在将返回T??(双包装可选).这样编译就可以了,因为可以将类型自由地提升为可选值,包括将可选值提升为双倍可选值.

Now this causes confusion because the closure that your pass to flatMap takes an element input and returns T?. Because T is Int?, this closure is now going to be returning T?? (a double wrapped optional). This compiles fine because types can be freely promoted to optionals, including optionals being promoted to double optionals.

因此,正在发生的事情是将数组中的Int?元素提升为Int??元素,然后flatMap将其展开为Int?.这意味着nil元素不会因为被双重包装而从arr1中过滤掉,并且flatMap仅在包装的第二层上起作用.

So what's happening is that your Int? elements in the array are getting promoted to Int?? elements, and then flatMap is unwrapping them back down to Int?. This means that nil elements can't get filtered out from your arr1 as they're getting doubly wrapped, and flatMap is only operating on the second layer of wrapping.

为什么arr2 能够被过滤掉nil的原因似乎是由于您传递给闭包的升级的结果>.因为您显式地将闭包的返回类型注释为Int?,所以闭包将隐式地从(Element) -> Int?提升为(Element) -> Int??(闭包返回类型可以与其他类型一样自由提升)–而不是元素 itself Int?升级到Int??,因为如果没有类型注释,闭包将被推断为(Element) -> Int??.

Why arr2 is able to have nil filtered out of it appears to be as a result of the promotion of the closure that you pass to flatMap. Because you explicitly annotate the return type of the closure to be Int?, the closure will get implicitly promoted from (Element) -> Int? to (Element) -> Int?? (closure return types can get freely promoted in the same way as other types) – rather than the element itself being promoted from Int? to Int??, as without the type annotation the closure would be inferred to be (Element) -> Int??.

此怪癖似乎允许nil避免被双重包装,因此允许flatMap对其进行过滤(不确定是否是预期的行为).

This quirk appears to allow nil to avoid being double wrapped, and therefore allowing flatMap to filter it out (not entirely sure if this is expected behaviour or not).

您可以在下面的示例中看到此行为:

You can see this behaviour in the example below:

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
}


故事的道德感

远离双重包装的选装件,它们可以为您带来超级混乱的行为!


Moral of the story

Steer away from double wrapped optionals, they can give you super confusing behaviour!

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

If you're using flatMap, then you should be expecting to get back [Int] if you pass in [Int?]. If you want to keep the optionality of the elements, then use map instead.

这篇关于带有元素的数组上的Swift flatMap是可选的,具有不同的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆