TL;DR
通过使用协议,您可以扩展 SequenceType 以计算非 nil 的数量。
let array: [Int?] = [1, nil, 3]
assert(array.realCount == 2)
如果您只想要代码,请向下滚动到下方的“解决方案”。
我需要做一些类似的事情来创建一个array.removeNils() extension 方法。
问题在于,当您尝试执行以下操作时:
extension SequenceType where Generator.Element == Optional { }
你得到:
error: reference to generic type 'Optional' requires arguments in <...>
extension SequenceType where Generator.Element == Optional {
^
generic type 'Optional' declared here
所以问题是,我们应该在<> 中添加什么类型?它不能是硬编码类型,因为我们希望它适用于任何东西,因此,我们需要像 T 这样的泛型。
error: use of undeclared type 'T'
extension SequenceType where Generator.Element == Optional<T> {
^
看起来没有办法做到这一点。然而,在协议的帮助下,你实际上可以做你想做的事:
protocol OptionalType { }
extension Optional: OptionalType {}
extension SequenceType where Generator.Element: OptionalType {
func realCount() -> Int {
// ...
}
}
现在它只适用于带有可选参数的数组:
([1, 2] as! [Int]).realCount() // syntax error: type 'Int' does not conform to protocol 'OptionalType'
([1, nil, 3] as! [Int?]).realCount()
最后一个难题是将元素与nil 进行比较。我们需要扩展OptionalType 协议以允许我们检查项目是否为nil。当然我们可以创建一个isNil() 方法,但是不向 Optional 添加任何东西是理想的。幸运的是,它已经有一个map function 可以帮助我们。
下面是 map 和 flatMap 函数的示例:
extension Optional {
func map2<U>(@noescape f: (Wrapped) -> U) -> U? {
if let s = self {
return f(s)
}
return nil
}
func flatMap2<U>(@noescape f: (Wrapped) -> U?) -> U? {
if let s = self {
return f(s)
}
return nil
}
}
注意map2(相当于map 函数)仅在self != nil 时返回f(s)。我们并不真正关心返回什么值,因此我们实际上可以让它返回 true 以清楚起见。为了使函数更易于理解,我为每个变量添加了显式类型:
protocol OptionalType {
associatedtype Wrapped
@warn_unused_result
func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
}
extension Optional: OptionalType {}
extension SequenceType where Generator.Element: OptionalType {
func realCount() -> Int {
var count = 0
for element: Generator.Element in self {
let optionalElement: Bool? = element.map {
(input: Self.Generator.Element.Wrapped) in
return true
}
if optionalElement != nil {
count += 1
}
}
return count
}
}
为了澄清,这些是泛型类型映射到的内容:
- OptionalType.Wrapped == Int
- SequenceType.Generator.Element == 可选
- SequenceType.Generator.Element.Wrapped == Int
- map.U == 布尔
当然,realCount 可以在没有所有这些显式类型的情况下实现,并且通过使用 $0 而不是 true 它可以防止我们需要在 map 函数中指定 _ in。
解决方案
protocol OptionalType {
associatedtype Wrapped
@warn_unused_result
func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
}
extension Optional: OptionalType {}
extension SequenceType where Generator.Element: OptionalType {
func realCount() -> Int {
return filter { $0.map { $0 } != nil }.count
}
}
// usage:
assert(([1, nil, 3] as! [Int?]).realCount() == 2)
要注意的关键是$0 是Generator.Element(即OptionalType),$0.map { $0 } 将其转换为Generator.Element.Wrapped?(例如Int?)。 Generator.Element 甚至 OptionalType 都无法与nil 相比,但Generator.Element.Wrapped? 可以与nil 相比。