检查类型是否实现协议

时间:2015-04-12 18:30:18

标签: swift generics protocols

我正在编写a library,为默认的Swift类型创建扩展。

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

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是否为Equatable

/// 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
}

其中isEquatableBool值,告诉我TEquatable。我怎么能找到这个?

1 个答案:

答案 0 :(得分:3)

目前在Swift中没有一种好方法可以做到这一点。*这就是像sorted这样的函数是自由函数的原因,或者在成员的情况下,采用谓词。您正在寻找的测试和转换方法的主要问题是Equatable和类似协议具有关联类型或依赖Self,因此只能在通用函数内使用约束。

我猜你的目标是调用者可以跳过提供比较器功能,因此如果可用,它将回退到Equatable?如果不是崩溃?这里的问题是函数在运行时确定某些东西(参数是Equatable),这在编译时确实应该是可以确定的。这不是很好 - 在编译时完全确定这些东西要好得多。

所以你可以编写一个需要Equatable的免费功能:

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]

然后如果您尝试使用的内容来调用它,则会出现编译时错误:

let incomparable = [1,2,3] as [Any]
distinct(incomparable)  // compiler barfs - Any isn’t Equatable

使用运行时方法,您只能在运行程序时找到它。

好消息是,也有好处。搜索每个元素的数组的问题是对于大型数组来说函数将非常慢,因为对于每个元素,必须线性搜索已经看到的元素的列表。如果您使用另一个要求元素为distinct的版本(Hashable通常为Equatable)来重载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 } } } ,则可以使用集合来跟踪它们:

Hashable

在编译时,编译器将选择最佳版本的函数并使用它。如果您的东西是可以清除的,那么该版本会被选中,如果它只是等同的,它将使用较慢的版本(这是因为Equatable继承自{{1}},并且编译器选择更专业的函数)。在编译时而不是运行时执行此操作意味着您不会为支票付任何罚款,所有这些都是事先确定的。

*有一些丑陋的方式,但由于目标是吸引人的语法,重点是什么......也许下一个版本将允许对方法进行约束,这将是不错的。