如何简化Swift Enum自定义初始化

如何简化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 //... }

我用字符串类型包装了一个枚举。它有两个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
        //...
        }
    }
    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