如何将扩展仅应用于泛型类型的某些特化?

时间:2014-06-24 21:10:53

标签: arrays generics swift

是否有办法将扩展应用于泛型类型,以使其符合协议,当该扩展仅对泛型类型的某些特化有效时?

例如,考虑这个协议,它返回一个字典,计算符合协议的实例所包含的值的频率:

// a type conforming to this protocol should return a dictionary
// which counts the frequencies of values contained by the type instance
protocol ConvertibleToFrequencyDictionary {
   typealias ItemType
   func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int>
}

由于要计数的值必须作为字典的键,因此值必须是符合Hashable的类型。这由通用dictionaryCountingFrequencies方法定义的类型约束表示。 (我没有看到任何方法直接在关联类型上定义类型约束,例如,在“typealias”声明中。)

现在考虑在Array上使用此扩展,以使其符合协议:

extension Array : ConvertibleToFrequencyDictionary {
  typealias ItemType=Element
  func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int> {
    var valueToCount = Dictionary<ItemType,Int>()
    for item in self {
      if let existingCount = valueToCount[item] {
        valueToCount.updateValue(value: existingCount + 1, forKey: item)
      } else {
        valueToCount.updateValue(value: 1, forKey: item)
      }
    }
    return valueToCount;
  }
}

这应该返回Array中出现的每个不同值的频率。但是,当然,由于这些值必须是可清除的,因此此扩展名仅在应用于Array<T:Hashable>时才有效。

然而,这对Array<Int>不起作用,即使Int是Hashable。

为什么不呢?如果您在泛型类型上编写扩展,那么该扩展是否必须能够适用于泛型类型的每个可能的特化?

1 个答案:

答案 0 :(得分:0)

您可以展开SequenceType而不是Array,这会为您提供更多适用的类型,然后使用Element限制where的类型。

  • Array个扩展名需要where Element : SomeProtocol
  • SequenceType个扩展名需要where Generator.Element : SomeProtocol

您遇到的问题不是Int不是Hashable,而是项目不一定是Hashable。对ItemType的强制转换也有效,因为在此示例中始终为Hashable

就个人而言,我不会使用通用的。由于Element已经涵盖了您可以存储在Array中的所有内容,因此无需将其设置为类型模糊。

extension SequenceType where Generator.Element : Hashable {

    func dictionaryCountingFrequencies() -> Dictionary<Generator.Element,Int> {
        var valueToCount = Dictionary<Generator.Element,Int>()
        for item in self {

            if let existingCount = valueToCount[item] {
                valueToCount.updateValue(existingCount + 1, forKey: item)
            } else {
                valueToCount.updateValue(1, forKey: item)
            }
        }
        return valueToCount;
    }
}

let array = [1,2,3,4,5,1,1,1,2,3,3]

let freqDict = array.dictionaryCountingFrequencies()
// prints [5: 1, 2: 2, 3: 3, 1: 4, 4: 1]
freqDict[5]
// prints 1