Generics 如何在Swift中确定泛型是否为可选?

Generics 如何在Swift中确定泛型是否为可选?,generics,swift,optional,Generics,Swift,Optional,我想用一个函数扩展一个数组,该函数将返回数组中所有非nil项的计数。理想情况下,这将适用于任何可选或非可选类型的数组。我尝试了多种编译失败的方法,崩溃了Xcode或两者兼而有之。我会假设它看起来像这样: extension Array { func realCount() -> Int { var cnt = 0 for value in self { if value != nil { cnt

我想用一个函数扩展一个数组,该函数将返回数组中所有非nil项的计数。理想情况下,这将适用于任何可选或非可选类型的数组。我尝试了多种编译失败的方法,崩溃了Xcode或两者兼而有之。我会假设它看起来像这样:

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

        return cnt
    }
}
斯威夫特抱怨说,
T
不能转换为
UInt8
。或者有时
MirrorDisposition
或其他随机类

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


编辑:从Xcode 6 beta 5开始,现在可以编译,但不会给出预期的结果<代码>如果值!=nil每次都计算为true。

您不能将任意值与
nil
进行比较(编辑:但请参见下面的Sulthan评论;可能我们应该能够将任意值与
nil
进行比较;本段的其余部分今天可能为true,但这只是由于编译器错误)。虽然
Optional
应用了一些语法糖分,但它实际上只是一个枚举,
nil
只是
可选。None
。您需要一种行为用于一种类型(
可选
),另一种行为用于所有其他类型。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
也就是说,如果我传递一组“可实现”的东西,然后根据它们的规则过滤它们。否则,就数一数。虽然我可能不会真正使用这个函数(这似乎是非常特殊的情况),但这个概念很有用。以后的调用方可以添加新的“realizable”类型,而无需修改任何代码(甚至不知道它们是如何实现的)。这展示了如何为那些没有实现协议的东西设置默认行为

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

TL;博士

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

let array: [Int?] = [1, nil, 3]
assert(array.realCount == 2)
如果您只需要代码,请向下滚动至下面的“解决方案”


我需要做一些类似的事情来创建一个方法

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

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
看来没办法了。但是,在协议的帮助下,您实际上可以做您想做的事情:

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
flatMap
函数的示例:

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
  }
}
为了澄清,以下是泛型类型映射到的对象:

  • OptionalType.Wrapped==Int
  • SequenceType.Generator.Element==可选
  • SequenceType.Generator.Element.Wrapped==Int
  • map.U==Bool
当然,realCount可以在没有所有这些显式类型的情况下实现,并且通过使用
$0
而不是
true
可以避免我们需要在
map
函数中指定


解决方案
协议选项类型{
关联类型包装
@警告未使用的结果
func地图(@noescape f:(包装)投掷->U)再次投掷->U?
}
扩展可选:OptionalType{}
扩展序列类型,其中生成器。元素:OptionalType{
func realCount()->Int{
返回筛选器{$0.map{$0}!=nil}.count
}
}
//用法:
断言([1,nil,3]as![Int?])。realCount()==2)

需要注意的关键是
$0
是一个
生成器.Element
(即
OptionalType
)和
$0.map{$0}
将其转换为
生成器.Element.Wrapped?
(例如Int?)
Generator.Element
甚至是
OptionalType
都不能与
nil
相比,但是
Generator.Element.Wrapped?
可以与
nil
相比对,我提出了一些不涉及扩展数组的方法,但是在类之外定义这些函数会减少它们之间的关联,你知道吗?无论如何,我希望也许我忽略了一些东西。这种泛型编程(而不是面向对象编程)在Swift stdlib中非常常见。此外,应该可以用序列而不是数组来编写realCount(),而且它会更强大(尽管我第一次尝试这样做失败了,所以仍然会弄乱),实际上,每种类型都应该与
nil
相当,因为每种类型都可以升级为可选类型。这是应该可以实现方法的情况之一。然而,我们又一次被那个讨厌的Swift错误所震惊,它使得与
nil
进行比较变得不可能了。@Sulthan编辑后指出了这一点;我相信你是对的,可选的升级应该使
nil
比较成为可能。注意
getLogicValue()
已被
hasValue
替换为你好,如果你能看看我类似的问题,我会很高兴的。
([1, 2] as! [Int]).realCount() // syntax error: type 'Int' does not conform to protocol 'OptionalType'
([1, nil, 3] as! [Int?]).realCount()
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
  }
}
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
  }
}
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)