Быстрый порядок перечисления и сравнение
У меня возникли проблемы с поиском/пониманием документации о том, как сравнивать перечисления в 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