3

经过一定的压力,我创建了以下通用函数:

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”]

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

我希望我可以这样声明:

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

但我得到了错误:

数组不可转换为 '@lvalue inout $T5'

我怀疑答案是“你这个白痴,做这个……”或者“你做不到”

会是哪个?:-D

4

4 回答 4

3

Array类声明如下:

public struct Array<Element>

从 Swift 2.0 开始,您只能为泛型Element参数符合协议的实例创建扩展方法:

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"]
于 2016-07-26T09:02:14.287 回答
2

Array 定义为Array<T>,表示一个 Array 可以容纳任何类型的 T,而 T 不一定符合Hashable协议。因此,您不能应用需要此约束的方法。不幸的是,你也不能检查或拒绝这个协议,因为Hashable 没有用@objc属性声明。

可以使用字典删除重复项,但不能使用 T 作为键,原因与上述相同。但是,如果 T 可以唯一地表示为字符串,则应该可以:

extension Array {
    func unique()->Array<T> {
        var buffer = Dictionary<String,T>()
        for it in self {
            buffer["\(it)"] = it
        }
        return Array(buffer.values)
    } 
}
于 2014-07-23T00:31:15.477 回答
2

您不能为泛型定义比泛型本身更具限制性的模板化方法(仅适用于某些形式的泛型)。

此外,当您在扩展方法中定义 T 时,您正在定义一个与数组中定义的 T 无关的新 T。无需重新定义 T ,最终这就是您遇到的问题。在泛型中定义新模板很好,但无法将这些模板与 T 和协议相关联。

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

于 2014-07-22T23:25:57.660 回答
0

这是另一种方法,尽管正如drawag建议的那样,这个方法返回一个副本。全局函数采用 any SequenceType,但总是返回 anArray因为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使用知道如何附加值的闭包来重载全局。深思熟虑。

于 2014-10-09T15:13:57.077 回答