【问题标题】:How to determine if a generic is an optional in Swift?如何确定泛型在 Swift 中是否是可选的?
【发布时间】:2014-07-31 16:49:28
【问题描述】:

我想使用一个函数来扩展一个数组,该函数将返回数组中所有非零项的计数。理想情况下,这将适用于任何可选或非可选类型的数组。我尝试了各种无法编译、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 每次都评估为真。

【问题讨论】:

    标签: generics swift optional


    【解决方案1】:

    您不能将任意值与 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)。


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

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

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

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

    【讨论】:

    • 对,我想出了一些不涉及扩展数组的方法,但是在类之外定义这些函数感觉就像它减少了它们之间的关联, 你懂的?无论如何,我希望也许只是我忽略了一些东西。
    • 这种泛型编程(而不是面向对象编程)在 Swift 标准库中很常见。此外,应该可以用序列而不是数组来编写 realCount(),它会更强大(虽然我第一次尝试这样做失败了,所以仍然搞砸了)
    • 实际上,每种类型都应该与nil 相当,因为每种类型都可以提升为 Optional。这是应该可以实现方法的情况之一。然而,我们再次被那个恼人的 Swift 错误所震惊,它使得与nil 进行比较是不可能的。
    • @Sulthan 编辑了这一点;我相信您是正确的,可选促销应该使nil 比较成为可能。
    • 注意 getLogicValue() 已替换为 hasValue
    【解决方案2】:

    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
    

    所以问题是,我们应该在&lt;&gt; 中添加什么类型?它不能是硬编码类型,因为我们希望它适用于任何东西,因此,我们需要像 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 函数)仅在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)
    

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

    【讨论】:

    【解决方案3】:

    检查泛型类型是否可选

    使用函数_isOptional(T.Type)

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

    适用于泛型或具体类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      • 2021-02-06
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多