Быстрый порядок перечисления и сравнение

У меня возникли проблемы с поиском/пониманием документации о том, как сравнивать перечисления в Swift по порядку определения. В частности, когда я создаю перечисление, такое как

enum EnumType {
    case First,  Second, Third
}

Swift не позволяет мне напрямую сравнивать перечисления по порядку, например

let type1 = EnumType.First
let type2 = EnumType.Second
if type1 < type2 {println("good")} // error

он генерирует ошибку компиляции "не может вызывать" <"с списком аргументов типа {EnumType, EnumType}. Таким образом, единственное решение, которое я нашел, - это написать мои собственные операторы сравнения в качестве перегрузок, таких как

enum EnumType : Int {
    case First = 0, Second, Third
}

func <(a: EnumType, b: EnumType) -> Bool {
    return a.rawValue < b.rawValue
}

let type1 = EnumType.First
let type2 = EnumType.Second
if type1 < type2 {println("good")} // Returns "good"

Это хорошо и полезно для перечней "тяжелых весов", которые имеют большую ценность и полезность в моем приложении, но перегрузка всех операторов, которые я могу использовать, кажется чрезмерно обременительной для "легких" перечислений, которые я могу определить "на лету" навести порядок на некоторые константы для одного небольшого модуля.

Есть ли способ сделать это, не написав много кода перегрузки шаблонов для каждого типа перечисления, который я определяю в своем проекте? Еще лучше, есть ли что-то, что мне не хватает, чтобы Swift автоматически предоставлял операторы сравнения для простых перечислений, у которых нет связанных типов, т.е. которые являются нетипизированными или напечатаны как Int? Свифт знает, как сравнивать Интс, так почему он не может сравнить enum Ints?

Ответы

Ответ 1

До тех пор, пока вы даете свой enum базовый тип, он будет соответствовать протоколу RawRepresentable.

Это означает, что вы можете написать общий оператор сравнения для любого типа, который является сырым представимым, и имеет грубый тип, сопоставимый, например:

func <<T: RawRepresentable where T.RawValue: Comparable>(a: T, b: T) -> Bool {
    return a.rawValue < b.rawValue
}

что будет означать, что у вашего перечисления автоматически будет < оператор:

enum E: Int {  // this would work with Double and String also
    // btw, no need to give a seed value of 0,
    // that happens automatically for Ints
    case A, B, C, D, E
}

E.A < E.C  // returns true

Единственный бит шаблона, который вы все еще должны сделать, это пометить ваш enum как Comparable если вы хотите использовать его с универсальными алгоритмами, которые требуют:

extension E: Comparable { }
// (no need for anything else - requirements are already fulfilled)

let a: [E] = [.C, .E, .A]
let b = sorted(a)
// b will now be [.A, .C, .E]

Приведение его в соответствие с Comparable также даст ему <=, > и >= операторы автоматически (предоставленные стандартной библиотекой).

Ответ 2

Это в какой-то степени тот же ответ, что и сам ОП. Это связано с небольшим количеством кода шаблона для каждого перечисления, который вы хотите быть сопоставимым, но я предпочитаю это, чем наличие некоторой внешней магической функции, которая обеспечивает сопоставимость со всеми перечислениями. Это может вызвать проблемы, если вы делаете быстрое копирование и вставку из одной программы в другую, а затем перечисление не работает, и вы не можете вспомнить, почему.

public enum LogLevel: Int, Comparable {
    case verbose
    case debug
    case info
    case warning
    case error
    case severe

    // Implement Comparable
    public static func < (a: LogLevel, b: LogLevel) -> Bool {
        return a.rawValue < b.rawValue
    }
}

РЕДАКТИРОВАТЬ:

Это в ответ на комментарий от @JasonMoore.

Сопоставимый не требует ==. Это требуется Equatable, а стандартная библиотека Swift автоматически предоставляет Equatable для большинства видов перечислений.

http://www.jessesquires.com/blog/swift-enumerations-and-equatable/

Что касается>, <= и> =, в документации Apple говорится, что они требуются Comparable, но обеспечивается реализация по умолчанию (на основе использования == и <, я предполагаю).

https://developer.apple.com/documentation/swift/comparable

Здесь немного кода, который я запускал в IBM Swift Sandbox - он компилируется и отлично работает с указанным выше определением.

let a : LogLevel = LogLevel.verbose
let b : LogLevel = LogLevel.verbose
let c : LogLevel = LogLevel.warning

print(a == b)  // prints true
print(a > c)  // prints false
print(a <= c)  // prints true