草庐IT

swift - 扩展泛型 Array<T> 以采用协议(protocol)

coder 2023-09-07 原文

假设我定义了这样一个协议(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
}

请注意,我已将 distanceDouble 返回更改为可选;你可以按照你的意愿处理这个,但是如果 selfother 数组的长度不同,或者类型 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/

有关swift - 扩展泛型 Array<T> 以采用协议(protocol)的更多相关文章

  1. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  5. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  6. ruby-on-rails - Nokogiri:使用 XPath 搜索 <div> - 2

    我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll

  7. arrays - 这是 Ruby 中 Array.fill 方法的错误吗? - 2

    这个问题在这里已经有了答案:Arraysmisbehaving(1个回答)关闭6年前。是否应该这样,即我误解了,还是错误?a=Array.new(3,Array.new(3))a[1].fill('g')=>[["g","g","g"],["g","g","g"],["g","g","g"]]它不应该导致:=>[[nil,nil,nil],["g","g","g"],[nil,nil,nil]]

  8. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

    我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

  9. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  10. ruby-on-rails - 没有参数的 `<<`(小于两倍)是什么意思? - 2

    我在一个我想在formtasticGem中覆盖的方法中找到了这个。该方法如下所示:defto_htmlinput_wrappingdohidden_field_html是什么意思?在第三行做什么?我知道它对数组有什么作用,但在这里我不知道。 最佳答案 你可以这样读:hidden_field_htmllabel_with_nested_checkbox是连接到hidden_​​field_html末尾的参数-为了“清晰”,他们将其分成两行 关于ruby-on-rails-没有参数的`

随机推荐