Swift 检查类型是否实现了协议

Swift 检查类型是否实现了协议,swift,generics,protocols,Swift,Generics,Protocols,我正在写一篇为默认Swift类型创建扩展的文章 我想检查一下我的数组扩展是否有某种类型实现了某种协议。例如,请参见此方法: extension Array { /// Compares the items using the given comparer and only returns non-equal values /// :returns: the first items that are unique according to the comparer func

我正在写一篇为默认Swift类型创建扩展的文章

我想检查一下我的数组扩展是否有某种类型实现了某种协议。例如,请参见此方法:

extension Array {
    /// Compares the items using the given comparer and only returns non-equal values
    /// :returns: the first items that are unique according to the comparer
    func distinct(comparer: (T, T) -> Bool) -> [T] {
        var result: [T] = []
        outerLoop: for item in self {
            for resultItem in result {
                if comparer(item, resultItem) {
                    continue outerLoop
                }
            }
            result.append(item)
        }
        return result
    }
}
现在我想重写这个方法来检查
T
是否是
equalable

/// Compares the items using the given comparer and only returns non-equal values
/// :returns: the first items that are unique according to the comparer
func distinct(comparer: ((T, T) -> Bool)?) -> [T] {
    var result: [T] = []
    outerLoop: for item in self {
        for resultItem in result {
            if isEquatable ? comparer!(item, resultItem) : item == resultItem {
                continue outerLoop
            }
        }
        result.append(item)
    }
    return result
}

其中,
isEquatable
是一个
Bool
值,它告诉我
T
是否是
相等的。我怎样才能找到答案呢?

目前在Swift中没有一个很好的方法来实现这一点。*这就是为什么像
sorted
这样的函数要么是自由函数,要么在成员的情况下是谓词。您正在寻找的测试和强制转换方法的主要问题是,
equalatable
和类似协议具有关联类型或依赖于
Self
,因此只能在泛型函数中用作约束

我猜您的目标是调用方可以跳过提供comparator函数,因此它将返回到
equalable
(如果可用)?如果不是的话,它会崩溃吗?这里的问题是函数在运行时确定某些内容(参数是
equalable
),而这实际上应该在编译时确定。这并不好——最好在编译时完全确定这些内容

因此,您可以编写一个自由函数,该函数需要
equalable

func distinct<C: CollectionType where C.Generator.Element: Equatable>
  (source: C) -> [C.Generator.Element] {

    var seen: [C.Generator.Element] = []
    return filter(source) {
        if contains(seen, $0) {
            return false
        }
        else {
            seen.append($0)
            return true
        }
    }   
}

let uniques = distinct([1,2,3,1,1,2])  // [1,2,3]
使用运行时方法,只有在运行程序时才能发现这一点

好消息是,也有好处。为每个元素搜索数组的问题是,对于大型数组,函数的速度会非常慢,因为对于每个元素,必须线性搜索已经看到的元素列表。如果您使用另一个版本重载
distinct
,该版本要求元素可以
散列
(通常是
相等
),您可以使用集合来跟踪它们:

func distinct<C: CollectionType where C.Generator.Element: Hashable>
  (source: C) -> [C.Generator.Element] {

    var seen: Set<C.Generator.Element> = []
    return filter(source) {
        if seen.contains($0) {
            return false
        }
        else {
            seen.insert($0)
            return true
        }
    }
}
func-distinct
(来源:C)->[C.Generator.Element]{
变量:Set=[]
返回过滤器(源){
如果看到。包含($0){
返回错误
}
否则{
见。插入($0)
返回真值
}
}
}
在编译时,编译器将选择函数的最佳版本并使用它。如果您的东西是可散列的,则会选择该版本,如果它是唯一可均衡的,则会使用较慢的版本(这是因为
可散列的
继承自
可均衡的
,编译器会选择更专门的函数)。在编译时而不是运行时执行此操作意味着您不必为检查付出任何代价,这是预先确定的


*有一些丑陋的方法,但既然目标是吸引人的语法,那又有什么意义呢……也许下一个版本将允许对方法进行约束,这会很好。

我猜您的目标是调用方可以跳过提供比较器函数,因此如果可用,它将返回到equalable?
是,但只有当两个
T
都不可
equalable
且给定的闭包为
nil
时才会崩溃。我明天会在某个地方查看这段代码,如果适用,我会接受。有没有办法在扩展中做到这一点?我不想扩大我的全球范围。。。特别是因为这是一个库。您可以创建一个
struct Collections{static func distinct etc..}
,然后用
Collections.distinct([1,2,3])
调用它,但请记住Swift已经有隐式的框架名称空间(即,如果您正在编写一个框架“CoolStuff”)然后此函数将是
CoolStuff.distinct
。这是Swift库的工作方式,即
filter
实际上是
Swift.filter
(但所有Swift项目都隐式导入Swift
)我真的想让扩展中的每个函数都使用mongoDB或Java Stream之类的数组。这不是Swift的工作方式。这也不是一个好方法,因为如果您将
distinct
作为
数组的扩展来编写,它只适用于
数组
,而不适用于
连续数组
等,这与自由函数不同t一些特性,如功能性,可能会出现(甚至可能在WWDC的6月份),在这一点上,您可能能够扩展协议,这正是您真正想要的。
func distinct<C: CollectionType where C.Generator.Element: Hashable>
  (source: C) -> [C.Generator.Element] {

    var seen: Set<C.Generator.Element> = []
    return filter(source) {
        if seen.contains($0) {
            return false
        }
        else {
            seen.insert($0)
            return true
        }
    }
}