如何简化Swift Enum自定义初始化
我用字符串类型包装了一个枚举。它有两个init方法。一个是带有rawValue的默认init方法,另一个是带有intValue的自定义init方法。我是这样写的。有没有简单的方法不使用两个开关箱如何简化Swift Enum自定义初始化,swift,Swift,我用字符串类型包装了一个枚举。它有两个init方法。一个是带有rawValue的默认init方法,另一个是带有intValue的自定义init方法。我是这样写的。有没有简单的方法不使用两个开关箱 enum Roman: String { case I,V,X,L,C,D,M var intValue: Int { switch self { case .I: return 1 //... }
enum Roman: String {
case I,V,X,L,C,D,M
var intValue: Int {
switch self {
case .I:
return 1
//...
}
}
init?(intValue: Int) {
switch intValue {
case 1:
self = .I
//...
default:
return nil
}
}
}
//Roman to Int
let number = "XXI".reversed()
.map { Roman(rawValue: String($0))?.intValue ?? 0 }
.reduce((total: 0, max: 0)) { result, value in
let newTotal = result.total + (value < result.max ? -value : value)
return (newTotal, max(result.max, value))
}.total
enum罗马:字符串{
案例I、V、X、L、C、D、M
var intValue:Int{
切换自身{
案例一:
返回1
//...
}
}
初始化?(intValue:Int){
开关intValue{
案例1:
self=.I
//...
违约:
归零
}
}
}
//罗马到整数
让number=“XXI”。反转()
.map{Roman(rawValue:String($0))?.intValue±0}
.reduce((总计:0,最大值:0)){result,中的值
设newTotal=result.total+(值
如果我正确理解了你想要的。。为什么不直接将值分配给枚举器案例?前
enum Roman: Int {
case I = 1
case V = 5
case X = 10
case L = 50
case C = 100
case D = 500
case M = 1000
}
在你的主课上呢
print(Roman.I.rawValue)
print(Roman(rawValue: 1))
通过为
Int
值和enum case
s之间的双向映射定义两个字典,可以摆脱switch
语句
enum Roman: String {
case I, V, X, L, C, D, M
private static let intValues:[Roman:Int] = [.I:1,.V:5,.X:10,.L:50,.C:100,.D:500,.M:1000]
private static let mappingDict:[Int:Roman] = Dictionary(uniqueKeysWithValues: Roman.intValues.map({ ($1, $0) }))
var intValue:Int {
return Roman.intValues[self]!
}
init?(intValue:Int){
guard let roman = Roman.mappingDict[intValue] else { return nil }
self = roman
}
}
Dávid pászor的答案没有错,但我确实喜欢ΒασληΔ的原始值思维。这似乎是一个非常自然的方法。所以我想把它们放在一起 首先,从ΒασίληΔ的代码开始,添加一个
intValue
别名,只是因为我觉得它读起来好一点
enum Roman: Int {
case I = 1
case V = 5
case X = 10
case L = 50
case C = 100
case D = 500
case M = 1000
var intValue: Int { return rawValue }
}
然后使用新的CaseIterable提供字符串查找:
extension Roman: CaseIterable {
enum Error: Swift.Error {
case invalid
}
init<S: StringProtocol>(_ string: S) throws {
guard let roman = Roman.allCases.first(where: { "\($0)" == string }) else {
throw Error.invalid
}
self = roman
}
init(_ character: Character) throws { try self.init(String(character)) }
}
我不太喜欢这种算法,因为它对无效输入的行为不稳定,但至少这个版本拒绝无效字符,而不是将其转换为0。否,不完全是,在我看来,有一个枚举是有点奇怪的,因为你可以有字母的组合。@JoakimDanielson,当它有字母的组合时,我有加入的逻辑。但我的疑问是,我如何才能编写一个简单的枚举,并能够使用String和intI初始化@JoakimDanielson,为什么不使用一个罗马数字解析器,例如,只使用字符串表示。最好创建一个对象并让该对象实现解析functionality@JoakimDanielson我不同意
Roman
可能是个糟糕的名字,但它似乎有一个romanDigital
enum非常有意义,然后由parser@Alexander好的,我可以得到它,但至少它不应该有任何init方法,但是从一开始就设置了一切Roman.intValues.map({($1,$0)})
在这里比使用值和键的zip
更好。这样,在集合上只有一次迭代。此外,也不能保证值和键的顺序相同。@user28434您完全正确,改进很大。更新了我的答案。@DávidPásztor我已经修改了答案并更新了问题。我使用的不是两个字典,而是一个tuple@arunsiva为什么要使用元组数组?Dictionary
由于元素查找时间恒定,因此工作速度更快。@arunsiva,使用Array
的查找将是O(n)
,使用Dictionary
的查找将是O(1)
。因此,平均而言,您的intValue
和init?(intValue:)
版本将花费更多的时间。我需要用“X”初始化才能得到10,用10初始化才能得到“X”。为什么要init()?1.我有var string:string=“X”;我需要从这个string2中获取Int;我需要从这个数ασληΔ中得到罗马字母字符串。没有解释如何使用字符串或字符初始化。在guard-let-roman=roman.allCases.first(其中:{($0)==string})中,否则{
我认为“\($0)
将给出它的原始值。使用let-roman=roman.allCases.first(其中:{($0)==string})的简单解决方案查找将是0(n),而Dávid Pászor的ans将是0(1)。不是?复杂性分析(big-O)对于较小的N值,没有任何关于性能的有用信息。7项列表的线性搜索平均需要4个相等检查。7项列表的字典搜索需要一个哈希操作,加上(很可能)1个相等性检查。哈希函数可能需要3个以上的额外相等性检查。字典还引入了大量内存开销,这可能会减少内存局部性(不是在这么小的字典上,而是在更大的字典上)。良好的局部性通常比算法复杂度(大O)更重要,中等尺寸。如果你想知道我对这个特定主题的长篇大论,请参阅44:30左右开始。:D
let number = try "XXI".reversed()
.map { try Roman($0).intValue }
.reduce((total: 0, max: 0)) { result, value in
let newTotal = result.total + (value < result.max ? -value : value)
return (newTotal, max(result.max, value))
}.total