如何确定泛型是否是Swift中的可选项



我想用一个函数来扩展数组,该函数将返回数组中所有非零项的计数。理想情况下,这将适用于任何可选或非可选类型的数组。我尝试了很多编译失败、Xcode崩溃或两者兼而有之的事情。我本以为它会是这样的:

extension Array {
    func realCount() -> Int {
        var cnt = 0
        for value in self {
            if value != nil {
                cnt++
            }
        }
        return cnt
    }
}

Swift抱怨T不能转换为UInt8。或者有时CCD_ 3或其他随机类。

那么,假设这是可能的,有什么诀窍呢?

编辑:从Xcode 6测试版5开始,现在已经编译了,但没有给出预期的结果。if value != nil每次都评估为true。

您无法将任意值与nil进行比较(编辑:但请参阅下面Sulthan的评论;可能我们应该能够将任意值nil进行比较;这段的其余部分今天可能是正确的,但这只是由于编译器错误(。虽然Optional应用了一些语法糖,但它实际上只是一个枚举,而nil只是Optional.None。一种类型(Optional(需要一种行为,所有其他类型需要另一种行为。Swift通过泛型实现了这一点,只是没有扩展。你必须把它变成一个功能:

func realCount<T>(x: [T?]) -> Int {
  return countElements(filter(x, { $0.getLogicValue() } ) )
}
func realCount<T>(x: [T]) -> Int {
  return countElements(x)
}
let l = [1,2,3]
let lop:[Int?] = [1, nil, 2]
let countL = realCount(l) // 3
let countLop = realCount(lop) // 2

这种方法要灵活得多。Optional只是您希望以这种方式平面映射的众多类型之一(例如,您可以使用相同的技术来处理Result(。


编辑:你可以通过为你认为"真实"的东西创建一个协议来进一步做到这一点。这样你就不必把它局限于选项。例如:

protocol Realizable {
  func isReal() -> Bool
}
extension Optional: Realizable {
  func isReal() -> Bool { return self.getLogicValue() }
}
func countReal<S:Collection>(x: S) -> S.IndexType.DistanceType {
  return countElements(x)
}
func countReal<S:Collection where S.GeneratorType.Element:Realizable>(x: S) -> Int {
  return countElements(filter(x, {$0.isReal()}))
}

这意味着,如果我通过一组"可实现"的东西,然后根据它们的规则过滤它们。否则,就数数吧。虽然我可能不会真正使用这个函数(这似乎是非常特殊的情况(,但这个概念很有用。稍后的调用者可以添加新的"可实现"类型,而无需修改任何代码(甚至不知道它们是如何实现的(。这展示了如何为那些没有实现您的协议的东西设置默认行为。

顺便说一句,我在这里使用Collections只是因为它们更容易计数(而且我对返回类型有点草率;注意一个是DistanceType,另一个是Int(。在基于集合的泛型函数上正确获取类型仍然有点棘手(而且经常会使编译器崩溃(。我怀疑这一切在下一个测试版中都会有所改善。

TL;博士

通过使用协议,可以扩展SequenceType来计算非nil的数量。

let array: [Int?] = [1, nil, 3]
assert(array.realCount == 2)

如果你只是想要代码,向下滚动到下面的"解决方案"。


我需要做一些类似的事情来创建一个array.removeNils()扩展方法。

问题是,当你试图做这样的事情时:

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功能可以帮助我们

以下是mapflatMap函数的示例:

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
  }
}

注意,如果self != nilmap2(相当于map函数(如何仅返回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,可以避免我们需要在MirrorDisposition0函数中指定_ 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)

需要注意的关键是,$0Generator.Element(即OptionalType(,$0.map { $0 }将其转换为Generator.Element.Wrapped?(例如Int?(。Generator.Element甚至OptionalType不能与nil相比较,但Generator.Element.Wrapped?可以与nil相比较。

检查泛型类型是否为可选类型

使用函数_isOptional(T.Type)

    _isOptional(Int.self) // -> false
    _isOptional(Int?.self) // -> true

适用于普通类型或混凝土类型。

最新更新