通用扩展的swift类型约束

时间:2014-07-22 20:46:07

标签: generics swift swift-extensions

经过一些压力之后,我创建了以下通用函数:

func removeDupes<T : Hashable > (inout inputCollection : [T] ) -> [T] {
  var hashMap = [T : Bool]()
  var output = 0
  for thing in inputCollection {
    if !hashMap[thing] {
      hashMap[thing] = true
      inputCollection[output++] = thing
    }
  }
  while (inputCollection.count > output) {
    inputCollection.removeLast()
  }
  return inputCollection
}

所以当你这样做时:

var names = ["Bob", "Carol", "Bob", "Bob", "Carol", "Ted", "Ted", "Alice", "Ted", "Alice"]
removeDupes(&names)

名称将包含:[“Bob”,“Carol”,“Ted”,“Alice”]

现在我想在Array上添加“removeDupes”作为扩展方法,我正在用语法进行挣扎,因为我必须将它限制为Hashable项的数组。

我希望我可以这样声明:

extension Array {
  func removeDupes< T : Hashable> () -> [T] {
    return removeDupes(&self)
  }
}

但我收到错误:

数组无法转换为'@lvalue inout $ T5'

我怀疑答案是“你这个白痴,做这个......”还是“你做不到”

它会是什么? :-D

4 个答案:

答案 0 :(得分:3)

public struct Array<Element> 类声明如下:

Element

自Swift 2.0以来,可以仅为通用extension Array where Element: Hashable { @warn_unused_result func removeDupes() -> [Element] { var hashMap = [Element : Bool]() var result = [Element]() for thing in self { if hashMap[thing] == nil { hashMap[thing] = true result.append(thing) } } return result } } 参数符合协议的实例创建扩展方法:

where

请注意扩展声明中的removeDupes语句。这样,Hashable方法仅针对let names = ["Bob", "Carol", "Bob", "Bob", "Carol", "Ted", "Ted", "Alice", "Ted", "Alice"] let uniqueNames = names.removeDupes() // returns ["Bob", "Carol", "Ted", "Alice"] 元素的数组声明:

{{1}}

答案 1 :(得分:2)

您无法为通用本身定义更具限制性的通用模板方法(仅适用于某些通用形式)。

此外,当您在扩展方法中定义T时,您定义的新T与数组中定义的T无关。没有必要重新定义T,最终这就是您遇到的问题。可以在泛型中定义新模板,但无法将这些模板与T和协议相关联。

您的原始函数是实现该函数的最佳方法,尽管返回没有重复项的数组副本而不是修改原始函数可能会更好。这使得代码更加清晰,并且对于多线程更好(这对于执行这么多处理的方法可能很重要)。

答案 2 :(得分:2)

数组定义为Array<T>,这意味着数组可以包含任何类型T,而T不一定符合Hashable协议。因此,您无法应用需要此约束的方法。不幸的是,您也无法检查或转发此协议,因为Hashable was not declared with the @objc attribute

可以使用字典删除重复项,但由于上述相同的原因,您不能使用T作为键。但是,如果T唯一可表示为String,则应该起作用:

extension Array {
    func unique()->Array<T> {
        var buffer = Dictionary<String,T>()
        for it in self {
            buffer["\(it)"] = it
        }
        return Array(buffer.values)
    } 
}

答案 3 :(得分:0)

这是另一种方法,尽管正如drewag所说,这个方法会返回一个副本。全局函数采用任何SequenceType,但始终返回Array,因为SequenceType是一种非常通用的类型,不允许附加值。

public func unique<H: Hashable, S: SequenceType where S.Generator.Element == H>(sequence: S) -> [H] {
    var hashMap = [H: Bool]()
    var result = [H]()
    for elem in sequence {
        let key = hashMap[elem]
        if key == nil {
            hashMap[elem] = true
            result.append(elem)
        }
    }
    return result
}

然后,在Array分机:

public func unique<H: Hashable>() -> [H] {
    return Yeah.unique(map({ $0 as H }))
}

我对此解决方案并不感到高兴,因为数组遍历了两次:一次将其映射到Hashable,然后再次迭代它以删除重复项。它的优点是通过委托全局函数,您可以非常快速地将unique方法添加到采用SequenceType的任何类中。我也不喜欢你被迫返回Array的事实。解决这个问题的方法可能是使用一个知道如何附加值的闭包来使全局unique重载。值得深思。