Как упростить пользовательскую инициализацию Swift Enum - PullRequest
2 голосов
/ 04 апреля 2019

Я создал Enum с типом String. У него есть два метода инициализации. Один из них - метод инициализации по умолчанию с rawValue, а другой - пользовательский метод инициализации с intValue. Я написал это так Есть ли простой способ не использовать два случая переключения?

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 ]

6 голосов
/ 04 апреля 2019

Вы можете избавиться от операторов switch, определив два словаря для двунаправленного отображения между значениями Int и enum case s.

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 голос
/ 04 апреля 2019

Нет ничего плохого в ответе Давида Пастора, но мне действительно нравятся необдуманные мысли Βασίλης Δ.Это просто кажется очень естественным подходом.Поэтому я хотел бы свести их воедино.

Во-первых, начиная с кода Βασίλης Δ., Добавляя псевдоним 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.

0 голосов
/ 04 апреля 2019

Если я правильно понял, что вы хотите .. Почему бы не назначить значения непосредственно регистрам?Пример.

    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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...