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

时间:2014-07-31 16:49:28

标签: generics swift optional

我想扩展一个带有函数的数组,该函数将返回数组中所有非零项的计数。理想情况下,这适用于任何可选或非可选类型的数组。我尝试了各种无法编译,崩溃的Xcode或两者兼而有之的东西。我原以为它看起来像这样:

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

        return cnt
    }
}

Swift在这里抱怨T无法转换为UInt8。或者有时候MirrorDisposition或其他随机类。

所以假设它可能,那么诀窍是什么?

编辑:从Xcode 6 beta 5开始,现在编译,但没有给出预期的结果。 if value != nil每次都会评估为真。

2 个答案:

答案 0 :(得分:13)

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

Optional

这种方法更灵活。 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 只是您想要以这种方式进行flatMap的众多类型中的一种(例如,您可以使用相同的技术来处理Result)。


编辑:您可以通过为您认为“真实”的事物创建协议来进一步实现这一目标。这样您就不必将此限制为Optionals。例如:

Optional

这就是说,如果我传递了一系列“可实现的”东西,那么就按照他们的规则过滤它们。否则,只计算它们。虽然我可能不会真正使用这个功能(看起来非常特殊),但这个概念很有用。以后的调用者可以添加新的“可实现”类型,而无需修改任何代码(甚至不知道它们是如何实现的)。这显示了如何为未实现协议的事物设置默认行为。

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

答案 1 :(得分:1)

TL; DR

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

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

如果您只想要代码,请向下滚动到&#34;解决方案&#34;下方。

我需要做类似的事情来创建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可以帮助我们。

以下是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
  }
}

请注意map2(相当于map函数)仅在f(s)时才返回self != nil。我们并不关心什么值会返回,所以为了清晰起见,我们实际上可以让它返回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 == Bool

当然,可以在没有所有这些显式类型的情况下实现realCount,并且使用$0代替true它可以阻止我们在_ in中指定map功能

解决方案

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进行比较。