我正在编写a library来创建默认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
是否是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
}
其中
isEquatable
是一个Bool
值,它告诉我T
是否Equatable
。我怎么能找到这个? 最佳答案
目前在swift中没有一个好的方法可以做到这一点。*这就是为什么像sorted
这样的函数要么是自由函数,要么对于成员来说是谓词。您所寻找的test-and-cast方法的主要问题是Equatable
和类似的协议具有关联的类型或依赖于Self
,因此只能在泛型函数中用作约束。
我猜您的目标是调用者可以跳过提供comparator函数,因此如果可用的话,它将返回到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
(通常情况下是这样),则可以使用集合来跟踪它们: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
}
}
}
在编译时,编译器将选择函数的最佳版本并使用它。如果你的东西是散列的,那么这个版本会被选择,如果它是可计算的,那么它将使用较慢的版本(这是因为
Equatable
继承自Hashable
,而编译器选择更专门的函数)。在编译时而不是运行时执行此操作意味着您不必为检查支付罚金,这都是预先确定的。*有一些丑陋的方法,但是由于目标是吸引人的语法,所以重点是什么…也许下一个版本将允许对方法进行约束,这将是很好的。
关于swift - 检查类型是否实现协议(protocol),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29593024/