多次调度Swift尝试:通用参数“ T”“ Cat”与“ Dog”的参数冲突

如何解决多次调度Swift尝试:通用参数“ T”“ Cat”与“ Dog”的参数冲突

我正在看Julia上有关多次派遣的视频,并且好奇是否可以在Swift中编写类似的内容。我看到Swift依赖编译器,Julia似乎在运行时确定类型,但是我也发现了一些我不了解Swift的知识。

当两个参数属于同一类型的Pet而不是一个是Cat而另一个是Dog时,为什么下面的函数encounters会起作用?

例如下面两个功能正常工作

encounters(Arturo,Gabby) // both Cat
encounters(Cc,Bb) // both Dog

但是这些会导致编译器错误

与通用参数“ T”(“猫”与“狗”)相矛盾的参数

encounters(Arturo,Bb) // Cat and Dog
encounters(Bb,Arturo) // Dog and Cat
protocol Pet: Equatable {
    var name: String { get }
}
struct Cat: Pet {
    let name: String
}
struct Dog: Pet {
    let name: String
}

let Arturo = Cat(name: "Arturo")
let Gabby = Cat(name: "Gabby")
let Bb = Dog(name: "Bb")
let Cc = Dog(name: "Cc")

func encounters<T: Pet>(_ a: T,_ b: T) {
    var verb: String
    switch (a,b) {
    case is (Cat,Dog):
        verb = meet(a as! Cat,b as! Dog)
    case is (Dog,Dog):
        verb = meet(a as! Dog,b as! Dog)
    case is (Cat,Cat):
        verb = meet(a as! Cat,b as! Cat)
    case is (Dog,Cat):
        verb = meet(a as! Dog,b as! Cat)
        
    default:
        fatalError()
    }
    
    print("\(a.name) meets \(b.name) and \(verb)")
}

func meet(_ a: Cat,_ b: Cat) -> String {
    return "Slinks"
}

func meet(_ a: Cat,_ b: Dog) -> String {
    return "Hisses"
}

func meet(_ a: Dog,_ b: Dog) -> String {
    return "Howles"
}

func meet(_ a: Dog,_ b: Cat) -> String {
    return "Barks"
}

解决方法

如果要创建具有符合协议类型的泛型函数,则需要一直使用该协议,因此不能在函数内部使用符合类型,因为可以将任何自定义类型设为符合遵守协议。

这是仅使用协议的示例解决方案

protocol Pet {
    var name: String { get }
    var same: String { get }
    var other: String { get }
}

符合协议的猫和狗

struct Cat: Pet {
    var name: String
    var other: String { "Slinks" }
    var same: String { "Hisses" }
}
struct Dog: Pet {
    let name: String
    var other: String { "Barks" }
    var same: String { "Howles" }
}

然后功能变为

func encounters<T: Pet,U: Pet>(_ a: T,_ b: U) {
    let verb: String
    if T.self == U.self {
        verb = a.same
    } else {
        verb = a.other
    }
    print("\(a.name) meets \(b.name) and \(verb)")
}

示例

let arturo = Cat(name: "Arturo")
let gabby = Cat(name: "Gabby")
let bb = Dog(name: "Bb")
let cc = Dog(name: "Cc")

encounters(arturo,bb)
encounters(bb,arturo)
encounters(gabby,arturo)
encounters(cc,bb)

Arturo遇到Bb和Slinks
Bb遇见Arturo和Barks
Gabby遇见Arturo和Hisses
抄送遇到Bb和Howles

,

我不会采用这种方法,但是它不起作用的原因是因为您将Equatable附加到Pet上。您可能会说“宠物应该与其他宠物具有可比性”之类的意思,但这并不意味着。这意味着符合Pet的类型本身必须是平等的。如果您删除了Equatable,则可以像克劳斯所说的那样在不使用泛型的情况下正常工作:

Tab

如此多的protocol Pet { ... } func encounters(_ a: Pet,_ b: Pet) { ... } 使Swift开发人员感到紧张,这是不必要的。 as!没错,但是它为编译器无法捕获的简单错误打开了大门。这种样式使编译器可以为您提供更多帮助。它仍然无法捕获丢失的案件。

as!

我绝对不喜欢这里的func encounters(_ a: Pet,_ b: Pet) { var verb: String switch (a,b) { case let (a as Cat,b as Dog): verb = meet(a,b) case let (a as Dog,b) case let (a as Cat,b as Cat): verb = meet(a,b) default: fatalError() } print("\(a.name) meets \(b.name) and \(verb)") } 。如果创建了一些新的Pet,此代码和Julia都将崩溃。那不是很好。但是我认为这不是这个问题的主要部分。在Swift中,您需要在默认分支中添加一些内容。在Julia中,您将为fatalError添加更通用的多重方法。

如果真的只有两个可能的Pets,那么您真的应该考虑枚举而不是动态调度,但是我再次认为这不是问题。

就像我说的那样,我不太喜欢这种方法。我认为这很冗长,并且为错误添加了很多地方。如果您想要这种动态类型查找,那么我将在数据中进行动态类型查找。

meet(a::Pet,b::Pet)

即使您需要函数,也可以将其扩展为将函数存储为数据(这是实现动态调度的一种非常强大的方法)。

将动作功能添加到Meeting中,而不只是动词:

struct Meeting {
    let lhs: Pet.Type
    let rhs: Pet.Type
    let verb: String

    func matches(_ lhs: Pet,_ rhs: Pet) -> Bool {
        self.lhs == type(of: lhs) && self.rhs == type(of: rhs)
    }
}

let meetings = [
    Meeting(lhs: Cat.self,rhs: Dog.self,verb: "Hisses"),Meeting(lhs: Dog.self,verb: "Howles"),Meeting(lhs: Cat.self,rhs: Cat.self,verb: "Slinks"),verb: "Barks"),]

func encounters(_ a: Pet,_ b: Pet) {
    let verb = meetings
        .first(where: { $0.matches(a,b) })?.verb
        ?? "passes by"
    print("\(a.name) meets \(b.name) and \(verb)")
}

现在,它开始看起来更像您的多方法,将所有专用逻辑放在一个位置:

struct Meeting {
    let lhs: Pet.Type
    let rhs: Pet.Type
    let action: (Pet) -> String  // Function that takes a Pet and gives a String

    func matches(_ lhs: Pet,_ rhs: Pet) -> Bool {
        self.lhs == type(of: lhs) && self.rhs == type(of: rhs)
    }
}

let meetings = [ Meeting(lhs: Cat.self,action: { "Hisses at \($0.name)" }),action: { "Howles at \($0.name)" }),action: { _ in "Slinks by" }),action: { "Barks at \($0.name)" }),] 称之为:

encounters

它不完全像多重方法一样强大,但是在编译时就更容易推断类型安全性。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?