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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (49)

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

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

        return cnt
    }
}

这里Swift抱怨说T不能兑换成UInt8。或有时MirrorDisposition或其他随机类。

所以假设它是可能的,有什么窍门?

提问于
用户回答回答于

TL; DR

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

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()方法,但不添加任何东西将是理想的。幸运的是,它已经有了一个可以帮助我们的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
  }
}

请注意如何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它可以防止我们需要_ inmap函数中指定。

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

用户回答回答于

你不能比较任意值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只是这种平面地图的许多类型之一。

你可以通过为你认为“真实”的东西创建一个协议来进一步考虑。这样你就不必把它限制在可选项中。例如:

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

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

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

扫码关注云+社区