繁体   English   中英

通用扩展的swift类型约束

[英]swift type constraints on generic 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

Array类声明如下:

public struct Array<Element>

由于雨燕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"]

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

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

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

Array定义为Array<T> ,这意味着Array可以保存任何类型T,并且T不一定符合Hashable协议。 因此,您无法应用需要此约束的方法。 不幸的是,您也无法检查或向下转换此协议,因为Hashable 未使用@objc属性声明

可以使用Dictionary删除重复项,但由于上面提到的相同原因,您不能使用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)
    } 
}

这是另一种方法,尽管正如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,然后再次迭代它以删除重复项。 它的优点是通过委托全局函数,您可以非常快速地向任何采用SequenceType类添加unique方法。 我也不喜欢你被迫返回一个Array的事实。 解决这个问题的方法可能是使用一个知道如何附加值的闭包来拥有全局unique的重载。 值得深思。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM