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



我已经用字符串类型装了一个枚举。它有两个初始化方法。一个是带有 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
您可以通过

Int值和enum case之间的双向映射定义两个字典来摆脱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á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。

如果我理解正确的话,你想要什么..为什么不直接将值分配给枚举器事例?前任。

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

最新更新