检查一个类型是否实现了一个协议
Check if a type implements a protocol
我正在编写 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
这样的函数要么是自由函数,要么在成员的情况下,取一个谓词。您正在寻找的测试和转换方法的主要问题是 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, [=10=]) {
return false
}
else {
seen.append([=10=])
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([=12=]) {
return false
}
else {
seen.insert([=12=])
return true
}
}
}
在编译时,编译器会选择函数的最佳版本并使用它。如果你的东西是可散列的,那个版本会被选择,如果它只是等同的,它会使用较慢的版本(这是因为 Hashable
继承自 Equatable
,编译器会选择更专业的函数)。在编译时而不是 运行 时执行此操作意味着您不会为检查支付任何罚款,这一切都是预先确定的。
*有丑陋的方法,但既然目标是吸引人的语法,那有什么意义呢……也许下一个版本会允许对方法进行约束,这会很好。
我正在编写 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
这样的函数要么是自由函数,要么在成员的情况下,取一个谓词。您正在寻找的测试和转换方法的主要问题是 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, [=10=]) {
return false
}
else {
seen.append([=10=])
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([=12=]) {
return false
}
else {
seen.insert([=12=])
return true
}
}
}
在编译时,编译器会选择函数的最佳版本并使用它。如果你的东西是可散列的,那个版本会被选择,如果它只是等同的,它会使用较慢的版本(这是因为 Hashable
继承自 Equatable
,编译器会选择更专业的函数)。在编译时而不是 运行 时执行此操作意味着您不会为检查支付任何罚款,这一切都是预先确定的。
*有丑陋的方法,但既然目标是吸引人的语法,那有什么意义呢……也许下一个版本会允许对方法进行约束,这会很好。