我有一个递归枚举,其中大多数情况都有相同类型的关联值:
indirect enum Location {
case Title(String?)
case Region(Location)
case Area(Location, Location)
case City(Location, Location)
case Settlement(Location, Location)
case Street(Location, Location)
case House(Location, Location)
}
我想要做的是形成一个很好的字符串描述,其中包括所有非零标题。
func getStringFromLocation(location: Location) -> String? {
var parts: [String?] = []
switch location {
case .Title(let title): return title
case .House(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
case .Street(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
case .Settlement(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
case .City(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
case .Area(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
case .Region(let title):
parts.append(getStringFromLocation(title))
}
return parts
.filter { $0 != nil }
.map { $0! }
.joinWithSeparator(", ")
}
问题在于,七个可能的案例中有五个完全相同,我有一堆复制粘贴的代码,正如我想的那样,并不好。如果我列举了一百个案例怎么办?
有没有办法写这样的东西?
switch location {
case .Title(let title):
parts.append(title)
case .Region(let title):
parts.append(getStringFromLocation(title))
default (let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
}
...使用一些默认情况来处理所有类似的情况?
答案 0 :(得分:1)
虽然我同意保罗的担心,以这种方式嵌套Location
很奇怪,但基本问题是可以解决的。就个人而言,我不会用default
来解决它,我只是简化代码并使用Swift给我们的工具(比如CustomStringConvertible
;我也在你的数据上添加标签;这太令人困惑了只有两个Location
元素具有完全不同的含义):
indirect enum Location: CustomStringConvertible {
case Title(String?)
case Region(Location)
case Area(title: Location, parent: Location)
case City(title: Location, parent: Location)
case Settlement(title: Location, parent: Location)
case Street(title: Location, parent: Location)
case House(title: Location, parent: Location)
var description: String {
func format(locs: (Location, Location)) -> String {
return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ")
}
switch self {
case .Title(let title): return title ?? ""
case .Region(let title): return "\(title)"
case .House(let data): return format(data)
case .Street(let data): return format(data)
case .Settlement(let data): return format(data)
case .City(let data): return format(data)
case .Area(let data): return format(data)
}
}
}
注意我如何将整个元组卸载到data
。你不必在模式匹配中打破元组。枚举永远不会有多个关联数据。他们总是只有一个:一个元组。 (函数也是如此。所有函数都接受一个值并返回一个值。该值可能恰好是一个元组。)
但如果你真的想摆脱重复return format(data)
,那么你可以通过Mirror
。 (你可以通过Mirror
来解决相当令人震惊的事情。在你做之前你应该非常小心。这种情况只是重复输入,而不是重复的逻辑。一些重复的输入不是你应该创造的很多东西删除的复杂性。)
以下是你的表现:
var description: String {
switch self {
case .Title(let title): return title ?? ""
case .Region(let title): return "\(title)"
default:
let m = Mirror(reflecting: self)
guard let locs = (m.children.first?.value as? (Location, Location)) else {
preconditionFailure("Unexpected data in enum. Probably missing a case somewhere.")
}
return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ")
}
}
这里的教训是,枚举的第一个孩子是其所有数据的元组。
但是使用Mirror
要脆弱得多(注意我打开了崩溃的可能性)。虽然枚举可能是一个很好的工具,但你仍然可能想重新考虑这个数据结构。
答案 1 :(得分:1)
对于这个老问题的现代读者来说,您实际上可以绑定当今几个案例中的相同关联值。只要类型匹配即可。
case .house(let title, let parent), .street(let title, let parent):
parts.append(getStringFromLocation(parent))
parts.append(getStringFromLocation(title))
答案 2 :(得分:0)
不,Swift的模式匹配没有办法匹配碰巧具有相同关联值的不同枚举值。这就是你当前问题的答案。
正如Rob明智地建议的那样,有可能从case语句中重构重复的代码 - 但是单个case语句不可能在枚举值和之间匹配提取相关的值。 / p>
您发现自己想要这样做的事实表明您可能想重新考虑您的枚举设计。许多案例之间存在共享行为和共享结构,但枚举案例应该是相互排斥和独立的。
也许Area,City,Settlement,Street和House都是同样的东西?
indirect enum Location {
case Title(String?)
case Region(Location)
case BinaryLocation(BinaryKind, Location, Location)
enum BinaryKind {
case Area
case City
case Settlement
case Street
case House
}
}
(我不明白这两个相关位置的含义是什么,但是既然你这样做了,我建议你提出比BinaryLocation
和BinaryKind
更多的解释性名称。)
对于枚举来说,这甚至可能不是一个合适的情况;例如,这样的事情可能会更好:
protocol Location {
var description: String { get }
}
struct Title: Location {
var title: String?
var description: String {
return title
}
}
// ... and one for Region, and then ...
protocol BinaryLocation {
var child0: Location { get }
var child1: Location { get }
}
extention BinaryLocation: Location {
var description: String {
return "\(child0), \(child1)"
}
}
// ...and then either individual structs for House, Street, etc., or
// an enum like BinaryKind above.
我不能说,因为我不知道你的整个情况。
我可以所说的是你对重复代码的关注是有效的,而这个具体问题是回过头来看看模型大图的线索。以挑剔型严格性问题作为重新思考您的建模选择的线索是“迅捷的方式:”