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

Cas*_*ser 22 generics optional swift

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

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

        return cnt
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特抱怨说这T是不可转换的UInt8.或者有时MirrorDisposition或其他随机类.

所以假设有可能,有什么诀窍?

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

Rob*_*ier 14

你无法比较任意值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
Run Code Online (Sandbox Code Playgroud)

这种方法更灵活.Optional只是这种方式中你想要flatMap的众多类型中的一种(例如,你可以使用相同的技术来处理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()}))
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这种通用编程(而不是面向对象的编程)在Swift stdlib中非常常见.而且,应该可以用Sequence而不是Array来编写realCount(),它会更强大(虽然我第一次尝试这样做失败了,所以仍然搞乱了) (2认同)
  • 实际上,每种类型都应与`nil`相媲美,因为每种类型都可以提升为Optional.这是应该可以实现方法的情况之一.然而,我们再次受到令人讨厌的Swift错误的攻击,这使得与`nil`进行比较是不可能的. (2认同)

Sen*_*ful 5

TL; 博士

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

let array: [Int?] = [1, nil, 3]
assert(array.realCount == 2)
Run Code Online (Sandbox Code Playgroud)

如果您只想要代码,请向下滚动到下面的“解决方案”。


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

问题是,当您尝试执行以下操作时:

extension SequenceType where Generator.Element == Optional { }
Run Code Online (Sandbox Code Playgroud)

你得到:

error: reference to generic type 'Optional' requires arguments in <...>
extension SequenceType where Generator.Element == Optional {
                                                  ^
generic type 'Optional' declared here
Run Code Online (Sandbox Code Playgroud)

所以问题是,我们应该在里面添加什么类型<>?它不能是硬编码类型,因为我们希望它适用于任何事情,因此,相反,我们想要一个像T.

error: use of undeclared type 'T'
extension SequenceType where Generator.Element == Optional<T> {
                                                           ^
Run Code Online (Sandbox Code Playgroud)

似乎没有办法做到这一点。然而,在协议的帮助下,你实际上可以做你想做的事:

protocol OptionalType { }

extension Optional: OptionalType {}

extension SequenceType where Generator.Element: OptionalType {
  func realCount() -> Int {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

现在它只适用于带有可选项的数组:

([1, 2] as! [Int]).realCount() // syntax error: type 'Int' does not conform to protocol 'OptionalType'
([1, nil, 3] as! [Int?]).realCount()
Run Code Online (Sandbox Code Playgroud)

拼图的最后一部分是将元素与 进行比较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
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意map2(等效于该map函数)如何仅返回f(s)if 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
  }
}
Run Code Online (Sandbox Code Playgroud)

澄清一下,这些是泛型类型映射到的内容:

  • 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)
Run Code Online (Sandbox Code Playgroud)

要注意的关键是它$0是 a Generator.Element(ie OptionalType)$0.map { $0 }并将其转换为 a Generator.Element.Wrapped?(例如 Int?)。Generator.Element甚至OptionalType不能比nil,但Generator.Element.Wrapped?可以比nil