假设我已经定义了这样一个协议:
protocol EuclideanPoint {
func distance(other: Self) -> Double
func dimension() -> UInt
}
现在我想扩展 [Float]
和 [Double]
以采用该协议 .
但是以下代码:
extension [Float]: EuclideanPoint {
func distance(other: [Float]) {
return Double(zip(self, other).map{a, b in pow(a-b,2)}.reduce(0, combine: +))
}
func dimension() {
return UInt(self.count)
}
}
因错误而无效
错误:必须在非专用泛型类型'Array'上声明约束扩展,其中约束由'where'子句指定
我发现了类似的问题(如this),但建议的解决方案是使用 extension CollectionType where Generator.Element == S { ... }
,但在此上下文中会导致错误:
错误:协议'CollectionType'只能用作通用约束,因为它具有Self或关联类型要求
这有什么解决方案吗?
编辑:
使用建议的解决方案:
protocol DoubleConvertibleType {
var doubleValue: Double { get }
}
extension Double : DoubleConvertibleType { var doubleValue: Double { return self } }
extension Float : DoubleConvertibleType { var doubleValue: Double { return Double(self) } }
extension CGFloat: DoubleConvertibleType { var doubleValue: Double { return Double(self) } }
extension Array where Element : DoubleConvertibleType {
func distance(other: Array) -> Double {
return Double(zip(self, other).map{ pow($0.0.doubleValue - $0.1.doubleValue, 2) }.reduce(0, combine: +))
}
func dimension() -> UInt {
return UInt(self.count)
}
}
给 [Double]
和 [Float]
.distance()
和 .dimension()
方法 . 但是 [Double]
或 [Float]
不能用来代替符合EuclideanPoint协议所需的东西,产生错误:
错误:类型'[Double]'不符合协议'EuclideanPoint'
3 回答
EDITED
以下解决方案有些通用,符合协议
EuclidianPoint
,并基于两个假设:我们允许在
EuclideanPoint
协议中为方法distance
的蓝图包含泛型类型约束,并且我们将使用泛型([T]
)而不是参数类型Self
. 但是,我们将确定(在编译时)[T]
与Self
(此处为[Double]
,[Float]
或[Int]
类型)的类型相同,并确定[T]符合协议EuclidianPoint
.你可以放弃这个特定应用程序中的函数编程技术,例如
.map
和.reduce
,并且只关注实现"generic array adopted to euclidian protocol" . Swift中的这些.map
,.reduce
等专长确实很整洁有用,但在许多应用程序中只是for-hood-for循环的包装器,所以你不会因为手动命令式的方式而失去任何性能 . 事实上,已知.reduce
由于重复的数组复制分配而执行非常不可选的,同时减少了数组(我不会在这里更多地介绍......) . 无论如何,也许你可以利用我的例子并将其调整回更具功能性的范例 .我们从一个自定义类型协议
MyTypes
开始,它将作为我们想要包含在我们的泛型中的类型的接口 . 我们还添加了略微更新的EuiclidianPoint
协议,其中我们使用协议MyTypes
作为distance (...)
函数蓝图中使用的泛型T
的类型约束 .请注意,我已将
Double
的distance
返回更改为可选项;您可以按照自己的意愿处理,但如果self
和other
数组的长度不同,或类型Self
和[T]
不同,则需要显示不符合 - 我将在此处使用nil
.我们现在可以通过
EuclidianPoint
协议实现Array
的扩展:请注意,在
distance
函数的内部if子句中,我们使用显式向下转换为T
,但由于我们断言Self
的元素的类型为T
,因此可以 .无论如何,有了这个,我们已经完成了,我们可以测试我们的"generic"数组扩展,我们现在注意到它也符合您的协议
EuclidianPoint
.好!
我的第一个答案仍然留有一条说明:
通用类型Array扩展到协议实际上最近才在这里被问到:
一致意见是你无法以"neat swifty"方式对数组进行通用扩展,这是你可能期望的 . 然而,有一些模拟这种行为的解决方法,一种是我上面使用的那种行为 . 如果您对其他方法感兴趣,我建议您查看此主题 .
前言:正如@difri在评论中正确提到的那样,在同时使用通用约束时,我们还无法创建符合协议的扩展 . 已经有几个雷达打开 - 搜索"extension of type with constraints cannot have an inheritance clause"会产生几个雷达 .
实际答案: Build 在@LeoDabus awesome answer和我的实验我想出了以下内容:
测试它
有点正确打印
要获得正确的答案(至少我怀疑),你必须将
distance
包装成sqrt
:然后正确打印
您可以扩展
SequenceType
而不是Array