我们说我已经定义了这样一个协议:
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)
}
}
由于错误,无效
错误:必须在非专用通用类型'数组'上声明约束扩展名。具有由'其中'指定的约束。条款
我发现了类似的问题(例如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'
答案 0 :(得分:1)
EDITED
以下解决方案有些通用,符合协议EuclidianPoint
,并基于两个假设:
我们允许在distance
协议中为方法EuclideanPoint
的蓝图包含泛型类型约束,而不是参数类型为{{1} },我们将使用通用(Self
)。但是,我们将确定(在编译时)[T]
与[T]
的类型相同(此处为Self
Self
,[Double]
或[Float]
类型),确定[T]符合协议[Int]
。
您确定我们将EuclidianPoint
和.map
等函数式编程技术排除在此特定应用程序之外,并专注于实现&#34 ; euclidian协议采用的通用数组" 。 Swift中的这些.reduce
,.map
等功能确实很整洁有用,但在许多应用程序中只是for-hood-for循环的包装器,所以你不会失去任何性能以手工命令式的方式做事。实际上,已知.reduce
由于重复的数组复制分配而执行非常不可选的,同时减少了数组(我不会在这里更多地进入此...)。无论如何,也许你可以利用我的例子并将其调整回更具功能性的范例。
我们从一个自定义类型协议.reduce
开始,它将作为我们想要包含在我们的泛型中的类型的接口。我们还添加了略微更新的MyTypes
协议,我们使用协议EuiclidianPoint
作为MyTypes
函数蓝图中使用的通用T
的类型约束。
distance (...)
请注意,我已将/* Used as type constraint for Generator.Element */
protocol MyTypes {
func -(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int : MyTypes { }
extension Double : MyTypes { }
extension Float : MyTypes { }
/* Extend with the types you wish to be covered by the generic ... */
/* Used as extension to Array : blueprints for extension method
to Array where Generator.Element are constrainted to MyTypes */
protocol EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double?
func dimension() -> UInt
}
Double
的回复更改为可选;您可以按照自己的意愿处理,但如果distance
和self
数组的长度不同,或类型other
和Self
不同,则需要显示不符合 - 我会在此处使用[T]
。
我们现在可以通过nil
协议实现我们对Array
的扩展:
EuclidianPoint
请注意,在/* Array extension by EuclideanPoint protocol */
extension Array : EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double? {
/* [T] is Self? proceed, otherwise return nil */
if let a = self.first {
if a is T && self.count == other.count {
var mySum: Double = 0.0
for (i, sElement) in self.enumerate() {
mySum += pow(((sElement as! T) - other[i]) as! Double, 2)
}
return sqrt(mySum)
}
}
return nil
}
func dimension() -> UInt {
return UInt(self.count)
}
}
函数的内部if子句中,我们使用显式向下转换为distance
,但因为我们断言T
的元素属于类型Self
,这没关系。
无论如何,有了这个,我们就完成了,我们可以测试我们的&#34;泛型&#34;我们现在注意到的数组扩展也符合您的协议T
。
EuclidianPoint
确定!
我的第一个答案仍然留有一条说明:
将泛型类型数组扩展到协议实际上最近才在这里提出:
一致意见是你无法在#34;整洁的swifty&#34;中执行数组到协议的通用扩展。你可能期望的方式。然而,有一些模拟这种行为的解决方法,一种是我上面使用的行为。如果您对其他方法感兴趣,我建议您查看此主题。
答案 1 :(得分:1)
您可以展开SequenceType
而不是Array
extension SequenceType where Generator.Element == Float {
//
}
答案 2 :(得分:1)
前言:正如@difri在评论中正确提到的那样,在同时使用通用约束时,我们还无法创建符合协议的扩展。已经有几个雷达打开 - 搜索“带有约束的类型扩展不能有继承子句”会产生几个雷达。
实际答案:在@LeoDabus awesome answer的基础上进行实验,我想出了以下内容:
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)
}
}
使用
进行测试let arr1 = [1.5, 2, 3]
let arr2 = [5.5, 2, 3]
let arrD = arr1.distance(arr2)
有些正确打印
16
要获得正确的答案(至少我怀疑),你必须将distance
包裹到sqrt
:
return sqrt(Double(zip(self, other).map{ pow($0.0.doubleValue - $0.1.doubleValue,2) }.reduce(0, combine: +)))
然后正确打印
4