如何解决多次调度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 举报,一经查实,本站将立刻删除。