扩展通用数组< T>采用协议 [英] extend generic Array<T> to adopt protocol
问题描述
pre $协议EuclideanPoint {
func distance(other:Self ) - > Double
func dimension() - > UInt
}
现在我想扩展 [Float ]
和 [Double]
来采用该协议。 但是下面的代码:
扩展名[Float]:EuclideanPoint {
func distance(other:[Float]){
return double(zip(self,other).map {a,b in pow(ab,2)}。reduce(0,combine:+))
}
func dimension(){
返回UInt(self.count)
}
}
无效,因为错误:
$ b
错误:约束扩展必须在非特定泛型类型'Array'上声明,约束由'where'子句指定 p>
我发现了类似的问题(如这个),但是建议的解决方案是使用扩展名CollectionType,其中Generato r.Element == S {...}
,但在这种情况下会导致错误:
error:protocol'CollectionType'只能用作通用约束,因为它具有自我或相关类型的要求
有没有解决方案到此?
编辑:
使用建议的解决方案:
protocol DoubleConvertibleType {
var doubleValue:Double {get}
}
扩展Double:DoubleConvertibleType {var doubleValue:Double {return self}}
扩展名浮点数:DoubleConvertibleType {var doubleValue:Double {return Double(self)}}
扩展名CGFloat:DoubleConvertibleType {var doubleValue:Double {return Double(self)}}
扩展数组元素: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协议,产生错误:
$ b
error:type'[Double]'不符合协议'EuclideanPoint'$ / b $ b
EDITED
以下解决方案有些通用,符合协议 我们可以在您的 我们从一个自定义类型协议开始, EuclidianPoint
,并且基于两个假设:
EuclideanPoint
中为方法
协议,而不是参数类型是距离
的蓝图包含泛型约束 Self
,我们将使用通用的( [T]
)。然而,我们将确定(在编译时) [T]
与 Self (和在这里,
自
[Double]
, [Float]
或 [Int]
type),并确认[T]符合协议 EuclidianPoint
.map
, .reduce
等特性确实很好用,但在许多应用程序中只是封装在引擎盖后面的循环中,所以你不会因为手动命令式的风格而失去任何性能。事实上, .reduce
由于在减少数组的同时重复数组拷贝分配而被认为是非常不可选的(我在这里不会详细讨论这个.. )。无论如何,也许你可以利用我的例子,并将其调整回更实用的范例。
$ b MyTypes
,它将作为我们希望包含在泛型中的类型的接口。我们还添加了稍微更新的 EuiclidianPoint
协议,我们在协议中使用协议 MyTypes
作为类型限制到通用 T
用于距离(...)
函数的蓝图。
/ *用作Generator.Element的类型约束* /
协议MyTypes {
func - (lhs:Self,rhs:Self) - > Self
func + =(inout lhs:Self,rhs:Self)
}
扩展名Int:MyTypes {}
扩展名Double:MyTypes {}
扩展名Float:MyTypes {}
/ *扩展您希望覆盖的类型... * /
/ *用作数组的扩展名:用于扩展方法的蓝图
将Array.Element约束为MyTypes的数组* * /
协议EuclideanPoint {
func distance< T:MyTypes> (其他:[T]) - >双?
func dimension() - > UInt
}
请注意,我更改了 现在我们可以通过 总之,我们完成了,我们可以测试我们的通用数组扩展,现在我们注意到它也符合您的协议 好! 我的第一个答案仍然存在: 通用类型数组扩展到协议实际上是最近在这里问的: 共识是你无法对数据进行通用的扩展,以一种你可能期望的整洁快捷的方式进行协议。但是,有一些解决方法可以模仿这种行为,其中之一就是我上面使用的行为。如果你对另一种方法感兴趣,我建议你看看这个线程。 Let's say I've defined such a protocol: Now I'd like to extend But the following code: is invalid because of the error error: constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause I found similar questions (like this), but the suggested solution is to use error: protocol 'CollectionType' can only be used as a generic constraint because it has Self or associated type requirements Is there any solution to this? EDIT: using the proposed solution: gives error: type '[Double]' does not conform to protocol 'EuclideanPoint'
EDITED The following solution is somewhat generic, conforms to protocol That we're allowed to include a generic type constraint for your blueprint for method That you're ok that we leave functional programming techniques such as We begin by a custom type protocol, Note that I've changed the We can now implement implement our extension of Note that in the inner if clause of the Anyway, with this, we're done, and we can test our "generic" array extensions, which we note now also conforms to your protocol Ok! A note still remaining from my first answer: Extension of generic type Array to protocol was actually just recently asked here: The consensus is the you cannot do a generic extension of array to a protocol in the "neat swifty" way that you possible expect. There are however workarounds to mimic such a behaviour, one being the one I've used above. If you are interested in another method, I suggest you look into this thread. 这篇关于扩展通用数组< T>采用协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! Double
返回距离
为可选;你可以像你一样处理,但如果 self 和
其他
数组的长度不同,或者类型 Self
和 [T]
不同,需要显示不符合项 - 我将使用 nil
在这里。
EuclidianPoint $ c>来实现我们对
Array
的扩展$ c $>协议:
$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
$ b func距离< T:MyTypes> (其他:[T]) - >双? {
/ * [T]是自我吗?继续,否则返回nil * /
如果让a = self.first {
如果a是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)
}
}
<请注意,在距离
函数的inner if子句中,我们使用明确的向下转换为 T
,但是我们断言 Self 的元素的类型是
T
,这是可以的。
EuclidianPoint $ c $
/ *测试和示例* /
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 size)
let _e = arr1i.distance(arr1d)// nil(Incomp。类型)
/ *用于函数调用:限制为
的通用数组参数符合协议'EuclidianPoint'的通用数组参数* /
func bar< T:MyTypes ,U:协议< EuclideanPoint,_ArrayType> U.Generator.Element == T> (arr1:U,_arr2:U) - >双? {
// ...
return arr1.distance(Array(arr2))
/ *我们需要明确地告诉距离函数
在这里我们发送一个数组,通过使用Array(..)初始化器* /
初始化一个
数组let myDist = bar(arr1d,arr2d)// 10 ,OK
protocol EuclideanPoint {
func distance(other: Self) -> Double
func dimension() -> UInt
}
[Float]
and [Double]
to adopt that protocol.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)
}
}
extension CollectionType where Generator.Element == S { ... }
, but in this context it leads to the error:
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]
and [Float]
the .distance()
and .dimension()
methods. Yet [Double]
or [Float]
cannot be used in place of something that is required to conform to the EuclideanPoint protocol, producing the error:
EuclidianPoint
, and is based upon two assumptions:
distance
in your EuclideanPoint
protocol, and that, instead of argument type being Self
, we'll use a generic ([T]
). We will, however, ascertain (at compile time) that [T]
is of the same type as Self
(and here, Self
of [Double]
, [Float]
or [Int]
type), and ascertain that [T] conforms to the protocol EuclidianPoint
..map
and .reduce
out of this specific application, and focus only on attaining a "generic array adopted to euclidian protocol". These .map
, .reduce
etc feats in Swift are indeed neat and useful, but are in many applications just wrappers for behind-the-hood for-loops, so you won't lose any performance over doing things in a manual imperative style. In fact, .reduce
is known to perform quite non-optional due to repeated array-copy-assignments while reducing the array (I won't go into this more here...). Anyway, perhaps you can make use of my example and tweak it back to something more functional-paradigmy.
MyTypes
, that will act as an interface for which types we want to include in our generic. We also add the slightly updated EuiclidianPoint
protocol, where we use protocol MyTypes
as a type restraint to the generic T
used in the distance (...)
function blue-print./* 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
return of distance
to an optional; you may handle this as you will, but in case the lengths of self
and other
arrays differ, or types Self
and [T]
differ, there will be some need of showing non-conformance -- I'll use nil
for this here. Array
by the EuclidianPoint
protocol:/* 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
function we use an explicit down cast to T
, but since we've asserted that elements of Self
are of type T
, this is ok.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