- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
假设我定义了这样一个协议(protocol):
protocol EuclideanPoint {
func distance(other: Self) -> Double
func dimension() -> UInt
}
现在我想扩展 [Float]
和 [Double]
以采用该协议(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)
}
}
因错误而无效
error: constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause
我发现了类似的问题(如 this ),但建议的解决方案是使用 extension CollectionType where Generator.Element == S { ... }
,但在这种情况下它会导致错误:
error: protocol 'CollectionType' can only be used as a generic constraint because it has Self or associated type requirements
有什么解决办法吗?
编辑:
使用建议的解决方案:
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 协议(protocol)所需的内容,从而产生错误:
error: type '[Double]' does not conform to protocol 'EuclideanPoint'
最佳答案
已编辑
以下解决方案有些通用,符合协议(protocol) EuclidianPoint
,并且基于两个假设:
我们被允许在您的 EuclideanPoint
协议(protocol)中为方法 distance
的蓝图包含一个通用类型约束,并且,而不是参数类型Self
,我们将使用泛型 ([T]
)。然而,我们将(在编译时)确定 [T]
与 Self
属于同一类型(此处,Self
属于 [Double]
、[Float]
或 [Int]
类型),确定 [T] 符合协议(protocol) 欧几里得点
。
您认为我们将 .map
和 .reduce
等函数式编程技术留在这个特定应用程序之外,您可以接受,而只专注于实现“采用欧几里得协议(protocol)的通用数组”。 Swift 中的这些 .map
、.reduce
等功能确实简洁实用,但在许多应用程序中只是 for 循环背后的包装器,所以你赢了以手动命令式方式做事会失去任何性能。事实上,众所周知,.reduce
在减少数组时由于重复的数组复制赋值而执行完全非可选的操作(我不会在这里详细介绍......)。无论如何,也许您可以利用我的示例并将其调整回更实用的范例。
我们从一个自定义类型协议(protocol)开始,MyTypes
,它将作为我们想要包含在我们的泛型中的类型的接口(interface)。我们还添加了稍微更新的 EuiclidianPoint
协议(protocol),其中我们使用协议(protocol) MyTypes
作为对 distance 中使用的通用
函数蓝图。T
的类型限制(...)
/* 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
}
请注意,我已将 distance
的 Double
返回更改为可选;你可以按照你的意愿处理这个,但是如果 self
和 other
数组的长度不同,或者类型 Self
和 [T ]
不同,将需要显示不符合项——我将在此处使用 nil
。
我们现在可以通过 EuclidianPoint
协议(protocol)实现我们对 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 子句中,我们使用了到 T
的显式向下转换,但是由于我们已经断言 Self 的元素
是 T
类型,这没问题。
无论如何,我们已经完成了,我们可以测试我们的“通用”数组扩展,我们注意到它现在也符合您的协议(protocol) 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
好的!
我的第一个答案还剩下一个注释:
将通用类型数组扩展到协议(protocol)实际上最近才在这里被问到:
共识是您不能以您可能期望的“简洁快捷”的方式对协议(protocol)进行数组的通用扩展。然而,有一些变通方法可以模仿这种行为,其中之一就是我在上面使用的方法。如果您对另一种方法感兴趣,我建议您查看此线程。
关于swift - 扩展泛型 Array<T> 以采用协议(protocol),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34475597/
考虑以下代码: template struct list { template list(Args...) { static_assert(sizeof..
考虑以下代码: template struct list { template list(Args...) { static_assert(sizeof..
最近才开始学习"new"OpenGL(可编程而不是固定功能,我从 Nehe 教程中学到的),我想知道自从 OpenGL 4 发布以来学习 OpenGL 3 是否真的有用。 我问的原因是因为我想知道能够
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我想了解如何操作特征向量/矩阵。我想实现最小二乘高斯牛顿算法(因此我学习使用 Eigen 库)。我有一个 1x6 的参数 vector ,每次迭代都需要更新它们。现在,我只想弄清楚函数如何将 vect
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 5 年前。 Improv
我发现编写适用于Enums的静态方法非常困难。这是一个非常人为的示例,但假设您想要编写一个方法,该方法采用 Enum 常量并返回下一个声明的常量。我发现(大约一个小时后)你可以按如下方式进行。它可以工
我正在尝试编写一个函数,在某些条件下,将指向结构的指针更改为指向不同的结构。 我的限制是我想保留初始函数签名,该签名将指向指针(而不是特定结构类型)的通用指针作为参数。 这行不通: [nav] In
我正在尝试将 Keras 示例改编为 VAE https://blog.keras.io/building-autoencoders-in-keras.html 我修改了代码,使用有噪声的 mnist
自 JPA 2.0 以来,关系上有 orphanRemoval 属性,它极大地简化了父子关系的更新,并且与级联删除一起允许删除树的整个分支并轻松删除它。 但是,也有一些情况可能被标记为“收养”,即您将
我正在尝试编写一个类,它能够在以后及时调用不带参数的 lambda。我期待 C++17 类模板参数推导以避免需要工厂函数。但是,尝试在不指定类型的情况下实例化对象会失败。我可以很好地使用工厂功能,但我
我怎样才能避免并非所有控制路径都在此处返回容器的事实: enum Type {Int, String}; Container containerFactory(Type
我开始学习 C++ 和 STL。 我有一个问题: 写一个函数模板palindrome,接受一个 vector 参数并返回true或false来检查 vector 是否是回文(12321是回文,1234
我一直在尝试获取一个条目值(代码中的 S1)以将其自身设置为一个值(_attributes 字典中的 STR),但我就是无法让它工作。我想让它成为一个最终的顶层循环,但我在这方面一步一步来,因为我是一
我想做同样的事情 How do I get the number of days between two dates in JavaScript? 但我想对此日期格式执行相同操作:2000-12-31
我想编写一个带有构造函数的 C++ 类,该构造函数将 auto_ptr 作为其参数,以便我可以将类实例从 auto_ptr 初始化为另一个实例: #include class A { public:
我需要一种方法,我可以在其中获取二维数组中的输入并以最快的方式之一对其进行逐行排序。我尝试使用 Insertion Sort 同时获取 Input 和 Sort it。我使用的第二件事是我单独为一行取
好的,我已经阅读了一些关于 IDisposable 最佳实践的文章,我想我基本上明白了(终于)。 我的问题与从 IDisposable 基类继承有关。我看到的所有示例都在子类中一遍又一遍地编写相同的代
定义类时,以下是否有效? T(const T&&) = default; 我正在阅读移动构造函数 here并且它解释了如何仍然可以隐式声明默认值: A class can have multiple
我想使用 LoadLibrary 开发一个插件系统。 我的问题是:我希望我的函数采用 const char* 而 LoadLibrary 采用 LPCTSTR。 我有一个聪明的想法来做(LPCSTR)
我是一名优秀的程序员,十分优秀!