假设我已经定义了这样的协议:
protocol EuclideanPoint { func distance(other: Self) -> Double func dimension() -> UInt }
现在,我想扩展[Float]并[Double]采用该协议。
[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”子句指定约束
我发现类似的问题,但建议的解决方案是使用extension CollectionType where Generator.Element == S { ... },但在这种情况下它会导致错误:
extension CollectionType where Generator.Element == S { ... }
错误:协议“ CollectionType”只能用作一般约束,因为它具有“自我”或相关类型要求
有什么解决办法吗?
编辑:
使用建议的解决方案:
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协议所需的内容,从而产生错误:
.distance()
.dimension()
错误:类型“ [Double]”不符合协议“ EuclideanPoint”
已编辑
以下解决方案在某种程度上是通用的,符合protocol EuclidianPoint,并且基于两个假设:
EuclidianPoint
我们可以distance在EuclideanPoint协议中为方法的蓝图包括通用类型约束,并且Self我们将使用通用([T])代替参数类型是。我们将然而,探明(在编译时间)[T]是相同类型的Self(和这里,Self的[Double],[Float]或[Int]类型),并 查明[T]符合协议EuclidianPoint。
distance
EuclideanPoint
Self
[T]
[Int]
那你真行,我们离开函数式编程技术,如.map和.reduce出这个特定的应用程序,而只注重敷脸 “采用欧几里德协议通用阵列” 。这些.map,.reduce等在斯威夫特的壮举确实是整洁和有用的,但在许多应用程序背后的引擎盖for循环只是包装,这样你就不会失去对在手动势在必行的风格做事任何性能。实际上,.reduce由于重复执行array-copy-assignments减少数组的同时,已知执行非可选的操作(我在这里不再赘述…)。无论如何,也许您可以利用我的示例并将其调整回更多的功能范例。
.map
.reduce
我们从一个自定义类型协议开始,该协议MyTypes将充当我们要包含在泛型中的类型的接口。我们还添加了稍微更新的EuiclidianPoint协议,在该协议中,我们将协议用作对功能蓝图中使用MyTypes的泛型的类型约束。T``distance (...)
MyTypes
EuiclidianPoint
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更改为可选值;您可以按照自己的意愿进行处理,但是如果selfand other数组的长度不同,或者类型Self和类型[T]不同,则需要显示不符合项-我将nil在此使用。
Double
self
other
nil
现在,我们可以Array通过EuclidianPoint协议来实现我们的扩展:
Array
/* 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) } }
请注意,在distance函数的内部if子句中,我们使用了显式的down强制转换为T,但是由于我们断言的元素Self是type T,所以可以。
T
无论如何,我们已经完成了,我们可以测试我们的“通用”数组扩展,我们现在注意到这些扩展也符合您的协议EuclidianPoint。
/* Tests and Examples */ let arr1d : [Double] = [3.0, 4.0, 0.0] let arr2d : [Double] = [-3.0, -4.0, 0.0] let arr3d : [Double] = [-3.0, -4.0] let arr1f : [Float] = [-3.0, -4.0, 0.0] let arr1i = [1, 2, 3] let _a = arr1d.dimension() // 3, OK let _b = arr1d.distance(arr2d) // 10, OK (A->B dist) let _c = arr1d.distance(arr1f) // nil (Incomp. types) let _d = arr1d.distance(arr3d) // nil (Incomp. sizes) let _e = arr1i.distance(arr1d) // nil (Incomp. types) /* for use in function calls: generic array parameters constrained to those that conform to protocol 'EuclidianPoint', as requested */ func bar<T: MyTypes, U: protocol<EuclideanPoint, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Double? { // ... return arr1.distance(Array(arr2)) /* We'll need to explicitly tell the distance function here that we're sending an array, by initializing an array using the Array(..) initializer */ } let myDist = bar(arr1d, arr2d) // 10, OK
好!
我的第一个答案中还剩下一条笔记:
实际上刚刚在这里要求将泛型类型Array扩展为协议:
共识是您 不能以 您期望的“简洁快速”的方式将 数组通用扩展到协议 。但是,有一些变通办法可以模仿这种行为,其中一个就是我上面使用的变通办法。如果您对另一种方法感兴趣,建议您研究此线程。