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

时间:2019-04-04 12:22:49

标签: swift

我创建了一个String类型的枚举。它有两种初始化方法。一个是带有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

3 个答案:

答案 0 :(得分:6)

您可以通过为switch值和Int之间的双向映射定义两个字典来摆脱enum case语句。

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
    }
}

答案 1 :(得分:1)

DávidPásztor的回答没有错,但我确实很喜欢ΒασίληςΔ。的思维原始价值。这似乎是很自然的方法。所以我想把它们放在一起。

首先,从ΒασίληςΔ。的代码开始,添加一个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)) }
}

有了这个,我认为number算法的顶部会更好一点:

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

我不是这种算法的忠实拥护者,因为它在输入无效时表现不正常,但至少此版本拒绝无效字符,而不是将其转换为0。

答案 2 :(得分:0)

如果我正确理解了您想要什么。为什么不直接将值分配给枚举器用例?例如。

    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))