当前位置: 首页 > 面试题库 >

扩展通用数组 采用协议

郁鸿博
2023-03-14
问题内容

假设我已经定义了这样的协议:

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”子句指定约束

我发现类似的问题,但建议的解决方案是使用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协议所需的内容,从而产生错误:

错误:类型“ [Double]”不符合协议“ EuclideanPoint”


问题答案:

已编辑

以下解决方案在某种程度上是通用的,符合protocol EuclidianPoint,并且基于两个假设:

  • 我们可以distanceEuclideanPoint协议中为方法的蓝图包括通用类型约束,并且Self我们将使用通用([T])代替参数类型是。我们将然而,探明(在编译时间)[T]是相同类型的Self(和这里,Self[Double][Float][Int]类型),并 查明[T]符合协议EuclidianPoint

  • 那你真行,我们离开函数式编程技术,如.map.reduce出这个特定的应用程序,而只注重敷脸 “采用欧几里德协议通用阵列” 。这些.map.reduce等在斯威夫特的壮举确实是整洁和有用的,但在许多应用程序背后的引擎盖for循环只是包装,这样你就不会失去对在手动势在必行的风格做事任何性能。实际上,.reduce由于重复执行array-copy-assignments减少数组的同时,已知执行非可选的操作(我在这里不再赘述…)。无论如何,也许您可​​以利用我的示例并将其调整回更多的功能范例。

我们从一个自定义类型协议开始,该协议MyTypes将充当我们要包含在泛型中的类型的接口。我们还html" target="_blank">添加了稍微更新的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更改为可选值;您可以按照自己的意愿进行处理,但是如果selfand
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)
    }
}

请注意,在distance函数的内部if子句中,我们使用了显式的down强制转换为T,但是由于我们断言的元素Self是type
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扩展为协议:

  • 通过遵循Swift 2中的协议来扩展类型数组

共识是您 不能以 您期望的“简洁快速”的方式将 数组通用扩展到协议
。但是,有一些变通办法可以模仿这种行为,其中一个就是我上面使用的变通办法。如果您对另一种方法感兴趣,建议您研究此线程



 类似资料:
  • 问题内容: public class MyGeneric {} 据我所知,以上示例中的两个子类均有效。我想知道Java如何知道何时在实例化子类时定义超类中给定的类型,以及何时将它们定义为实际的类名(即,它如何知道T,E不是类名)? 旁注,是否允许(即使不常见)对通用类型使用多个字母?如果(由于某些严重的计划错误)类型与现有类冲突,该怎么办? 那会发生什么呢? 编辑:感谢您的及时答复。为了回答我的第

  • 扩展说明 RPC 协议扩展,封装远程调用细节。 契约: 当用户调用 refer() 所返回的 Invoker 对象的 invoke() 方法时,协议需相应执行同 URL 远端 export() 传入的 Invoker 对象的 invoke() 方法。 其中,refer() 返回的 Invoker 由协议实现,协议通常需要在此 Invoker 中发送远程请求,export() 传入的 Invoker

  • 协议和扩展 你可以扩展一个已经存在的类型来采纳和遵循一个新协议, 就算是你无法访问现有类型的源代码也行. 扩展可以添加新的属性、方法和下标到已经存在的类型, 并且因此允许你添加协议需要的任何需要. protocol TextRepresentable { var textualDescription: String { get } } // 此处并无Dice这个类, 以及其sides属性

  • 问题内容: 我知道您不能动态扩展普通数组,但这是一种有效的方法吗? 我知道比尝试使用普通数组更好的方法,但是我想首先使用普通数组来解决这个问题。 我的愿望是,它从0 + 1(so 1)开始,在被称为new时,它的大小与相同,然后保持不变,同时再次声明,然后将其复制回新的大小。这对我来说很有意义,但我总是会遇到异常情况? 问题答案: 该方法不会更改OrigArray的值;它所做的只是在其中存储一个克

  • Array.from() Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。 下面是一个类似数组的对象,Array.from将它转为真正的数组。 let arrayLike = { '0': 'a', '1': 'b', '2': 'c',

  • 据我所知,协议缓冲区主要用于控制服务器和客户端代码的项目。我的一般问题是——协议缓冲区能否用于将二进制消息序列化/反序列化到使用现有协议的服务器?所以,我的问题: > 如果协议缓冲区不支持本机微调现有协议的序列化/反序列化方式,那么可以通过扩展添加该功能吗?是否可以以某种方式添加序列化/反序列化方法可以识别的关键字?也许这可以通过扩展或修改protobuf csharp port或protobuf