Как перечислить перечисление со строковым типом?
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
например, как я могу сделать что-то вроде:
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
приводя пример:
♠
♥
♦
♣
30 ответов:
Swift 4.2+
начиная с Swift 4.2 (С Xcode 10), просто добавьте соответствие протокола
CaseIterable
чтобы извлечь выгоду изallCases
:extension Suit: CaseIterable {}
тогда это выведет все возможные значения:
Suit.allCases.forEach { print(.rawValue) }
совместимость с более ранними версиями Swift (3.x и 4.x)
просто имитируйте реализацию Swift 4.2:
#if !swift(>=4.2) public protocol CaseIterable { associatedtype AllCases: Collection where AllCases.Element == Self static var allCases: AllCases { get } } extension CaseIterable where Self: Hashable { static var allCases: [Self] { return [Self](AnySequence { () -> AnyIterator<Self> in var raw = 0 var first: Self? return AnyIterator { let current = withUnsafeBytes(of: &raw) { .load(as: Self.self) } if raw == 0 { first = current } else if current == first { return nil } raw += 1 return current } }) } } #endif
этот пост актуален здесь https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift
по существу предлагаемое решение
enum ProductCategory : String { case Washers = "washers", Dryers = "dryers", Toasters = "toasters" static let allValues = [Washers, Dryers, Toasters] } for category in ProductCategory.allValues{ //Do something }
я сделал функцию полезности
iterateEnum()
для итерационных случаев для произвольногоenum
типы.пример использования:
enum Suit:String { case Spades = "♠" case Hearts = "♥" case Diamonds = "♦" case Clubs = "♣" } for f in iterateEnum(Suit) { println(f.rawValue) }
выходы:
♠ ♥ ♦ ♣
но это только для отладки или тестирования цель: это зависит от нескольких недокументированных текущих (Swift1.1) поведения компилятора. Так что используйте его на свой страх и риск :)
вот код:
func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> { var cast: (Int -> T)! switch sizeof(T) { case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self))) case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: ), T.self) } case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: ), T.self) } case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: ), T.self) } case 8: cast = { unsafeBitCast(UInt64(), T.self) } default: fatalError("cannot be here") } var i = 0 return GeneratorOf { let next = cast(i) return next.hashValue == i++ ? next : nil } }
основная идея это:
- представление памяти
enum
- кромеenum
s с соответствующими типами-это просто индекс случаев, когда количество случаев2...256
, он идентиченUInt8
, когда257...65536
, этоUInt16
и так далее. Так что, это может бытьunsafeBitcast
из соответствующих целочисленных типов без знака..hashValue
значений перечисления совпадает с индексом случая..hashValue
из перечисления значений bitcasted от недействительным это0
добавлено:
пересмотрены для Swift2 и реализованы идеи кастинга от @Kametrixom это
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> { var i = 0 return anyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>().memory } return next.hashValue == i++ ? next : nil } }
добавлено: Пересмотрено для Swift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(to: &i) { .withMemoryRebound(to: T.self, capacity: 1) { .pointee } } if next.hashValue != i { return nil } i += 1 return next } }
добавлено: Пересмотренный для Swift3.0.1
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafeBytes(of: &i) { .load(as: T.self) } if next.hashValue != i { return nil } i += 1 return next } }
другие решения работа но все они делают предположения, например, о количестве возможных рангов и мастей, или о том, что может быть первым и последним рангом. Правда, макет колоды карт, вероятно, не сильно изменится в обозримом будущем. В целом, однако, более аккуратно писать код, который делает как можно меньше предположений. Мое решение:
я добавил необработанный тип в перечисление костюма, поэтому я могу использовать костюм (rawValue:) для доступа к костюму случаи:
enum Suit: Int { case Spades = 1 case Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } func color() -> String { switch self { case .Spades: return "black" case .Clubs: return "black" case .Diamonds: return "red" case .Hearts: return "red" } } } enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func simpleDescription() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.rawValue) } } }
ниже реализации метода Createdeck () карты. init (rawValue:) является отказоустойчивым инициализатором и возвращает необязательный. Развернув и проверив его значение в обоих операторах while, нет необходимости принимать количество рангов или исков:
struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } func createDeck() -> [Card] { var n = 1 var deck = [Card]() while let rank = Rank(rawValue: n) { var m = 1 while let suit = Suit(rawValue: m) { deck.append(Card(rank: rank, suit: suit)) m += 1 } n += 1 } return deck } }
вот как вызвать метод createDeck:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs) let deck = card.createDeck()
второй ответ, который действительно работает
поэтому я споткнулся в битах и байтах и создал расширение (что я позже узнал, работает очень похоже на @rintaro'ы ответ). Это можно использовать следующим образом:
enum E : EnumCollection { case A, B, C } Array(E.cases()) // [A, B, C]
замечательно, что он может использоваться на любом перечислении (без связанных значений). Обратите внимание, что это не работает для перечислений, которые не имеют случаев.
отказ от ответственности
С @rintaroответ, этот код использует основное представление перечисления. Это представление не документировано и может измениться в будущем, что нарушит его -> я не рекомендую использовать это в производстве.
код (Swift 2.2, Xcode 7.3.1, не работает на Xcode 10)
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence<Self> { typealias S = Self return AnySequence { () -> AnyGenerator<S> in var raw = 0 return AnyGenerator { let current : Self = withUnsafePointer(&raw) { UnsafePointer().memory } guard current.hashValue == raw else { return nil } raw += 1 return current } } } }
код (Swift 3, Xcode 8.1, не работает на Xcode 10)
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence<Self> { typealias S = Self return AnySequence { () -> AnyIterator<S> in var raw = 0 return AnyIterator { let current : Self = withUnsafePointer(to: &raw) { .withMemoryRebound(to: S.self, capacity: 1) { .pointee } } guard current.hashValue == raw else { return nil } raw += 1 return current } } } }
(Я понятия не имею, зачем я нужен
typealias
, но компилятор жалуется и без него)(Я сделал большие изменения в ответьте, посмотрите на правки для прошлых версий)
вы можете перебирать перечисление, реализуя
ForwardIndexType
протокол.The
ForwardIndexType
протокол требует, чтобы вы определитьsuccessor()
функция для перехода через элементы.enum Rank: Int, ForwardIndexType { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King // ... other functions // Option 1 - Figure it out by hand func successor() -> Rank { switch self { case .Ace: return .Two case .Two: return .Three // ... etc. default: return .King } } // Option 2 - Define an operator! func successor() -> Rank { return self + 1 } } // NOTE: The operator is defined OUTSIDE the class func + (left: Rank, right: Int) -> Rank { // I'm using to/from raw here, but again, you can use a case statement // or whatever else you can think of return left == .King ? .King : Rank(rawValue: left.rawValue + right)! }
итерация по открытому или закрытому диапазону (
..<
или...
) будет внутренне называемsuccessor()
функция, которая позволяет писать так:// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits for r in Rank.Ace...Rank.King { // Do something useful }
в принципе это можно сделать таким образом, предполагая, что вы не используете назначение необработанных значений для случаев перечисления:
enum RankEnum: Int { case Ace case One case Two } class RankEnumGenerator : Generator { var i = 0 typealias Element = RankEnum func next() -> Element? { let r = RankEnum.fromRaw(i) i += 1 return r } } extension RankEnum { static func enumerate() -> SequenceOf<RankEnum> { return SequenceOf<RankEnum>({ RankEnumGenerator() }) } } for r in RankEnum.enumerate() { println("\(r.toRaw())") }
Обновлено Swift 2.2+
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> { var i = 0 return AnyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>().memory } if next.hashValue == i { i += 1 return next } else { return nil } } }
это обновленный код для Swift 2.2 формы @Kametrixom-этоswer
на Swift 3.0+ (огромное спасибо @Philip)
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(&i) { UnsafePointer<T>().pointee } if next.hashValue == i { i += 1 return next } else { return nil } } }
если вы даете перечисление необработанное значение Int это сделает цикл намного проще.
например, вы можете использовать
anyGenerator
чтобы получить генератор, который может перечислять ваши значения:enum Suit: Int, CustomStringConvertible { case Spades, Hearts, Diamonds, Clubs var description: String { switch self { case .Spades: return "Spades" case .Hearts: return "Hearts" case .Diamonds: return "Diamonds" case .Clubs: return "Clubs" } } static func enumerate() -> AnyGenerator<Suit> { var nextIndex = Spades.rawValue return anyGenerator { Suit(rawValue: nextIndex++) } } } // You can now use it like this: for suit in Suit.enumerate() { suit.description } // or like this: let allSuits: [Suit] = Array(Suit.enumerate())
однако, это выглядит как довольно распространенный шаблон, не было бы неплохо, если бы мы могли сделать любой тип перечисления перечислимым, просто соответствуя протоколу? Ну с Swift 2.0 и расширений протокола, теперь мы можем!
просто добавьте это в ваш проект:
protocol EnumerableEnum { init?(rawValue: Int) static func firstValue() -> Int } extension EnumerableEnum { static func enumerate() -> AnyGenerator<Self> { var nextIndex = firstRawValue() return anyGenerator { Self(rawValue: nextIndex++) } } static func firstRawValue() -> Int { return 0 } }
теперь каждый раз, когда вы создаете перечисление (пока оно имеет int raw значение), вы можете сделать его перечислимым, выполнив протокол:
enum Rank: Int, EnumerableEnum { case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King } // ... for rank in Rank.enumerate() { ... }
если ваши значения перечисления не начинаются с
0
(по умолчанию), переопределитьfirstRawValue
способ:enum DeckColor: Int, EnumerableEnum { case Red = 10, Blue, Black static func firstRawValue() -> Int { return Red.rawValue } } // ... let colors = Array(DeckColor.enumerate())
окончательный класс костюма, включая замену
simpleDescription
С более стандартный CustomStringConvertible протокол, будет выглядеть это:enum Suit: Int, CustomStringConvertible, EnumerableEnum { case Spades, Hearts, Diamonds, Clubs var description: String { switch self { case .Spades: return "Spades" case .Hearts: return "Hearts" case .Diamonds: return "Diamonds" case .Clubs: return "Clubs" } } } // ... for suit in Suit.enumerate() { print(suit.description) }
EDIT:
Swift 3
синтаксис:protocol EnumerableEnum { init?(rawValue: Int) static func firstRawValue() -> Int } extension EnumerableEnum { static func enumerate() -> AnyIterator<Self> { var nextIndex = firstRawValue() let iterator: AnyIterator<Self> = AnyIterator { defer { nextIndex = nextIndex + 1 } return Self(rawValue: nextIndex) } return iterator } static func firstRawValue() -> Int { return 0 } }
Мне нравится это решение, которое я собрал после нахождения этой странице: понимание списка в Swift
Он использует Int raws вместо строк, но он избегает ввода дважды, он позволяет настраивать диапазоны и не жестко кодирует необработанные значения.
enum Suit: Int { case None case Spade, Heart, Diamond, Club static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue static let allCases = Array(allRawValues.map{ Suit(rawValue: )! }) } enum Rank: Int { case Joker case Two, Three, Four, Five, Six case Seven, Eight, Nine, Ten case Jack, Queen, King, Ace static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue static let allCases = Array(allRawValues.map{ Rank(rawValue: )! }) } func makeDeck(withJoker withJoker: Bool) -> [Card] { var deck = [Card]() for suit in Suit.allCases { for rank in Rank.allCases { deck.append(Card(suit: suit, rank: rank)) } } if withJoker { deck.append(Card(suit: .None, rank: .Joker)) } return deck }
я обнаружил, что делаю
.allValues
много во всем моем коде. Я, наконец, понял, как просто соответствоватьIteratable
протокол иrawValues()
метод.protocol Iteratable {} extension RawRepresentable where Self: RawRepresentable { static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(to: &i) { .withMemoryRebound(to: T.self, capacity: 1) { .pointee } } if next.hashValue != i { return nil } i += 1 return next } } } extension Iteratable where Self: RawRepresentable, Self: Hashable { static func hashValues() -> AnyIterator<Self> { return iterateEnum(self) } static func rawValues() -> [Self.RawValue] { return hashValues().map({.rawValue}) } } // Example enum Grocery: String, Iteratable { case Kroger = "kroger" case HEB = "h.e.b." case Randalls = "randalls" } let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery> let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
enum Rank: Int { ... static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: )! } } enum Suit { ... static let suits = [Spades, Hearts, Diamonds, Clubs] } struct Card { ... static func fullDesk() -> [Card] { var desk: [Card] = [] for suit in Suit.suits { for rank in Rank.ranks { desk.append(Card(rank: rank,suit: suit)) } } return desk } }
Как насчет этого?
изменить: Быстрое Эволюционное ПредложениеSE-0194 производная коллекция случаев перечисления предлагает уровень возглавил решение этой проблемы. Мы видим это в Swift 4.2 и новее. Предложение также указывает на некоторые решения которые похожи на некоторые уже упомянутые здесь, но это может быть интересно увидеть, тем не менее.
Я также сохраню свой первоначальный пост для полноты картины.
Это еще один подход, основанный на ответе @Peymmankh, адаптированный к Swift 3.
public protocol EnumCollection : Hashable {} extension EnumCollection { public static func allValues() -> [Self] { typealias S = Self let retVal = AnySequence { () -> AnyIterator<S> in var raw = 0 return AnyIterator { let current = withUnsafePointer(to: &raw) { .withMemoryRebound(to: S.self, capacity: 1) { .pointee } } guard current.hashValue == raw else { return nil } raw += 1 return current } } return [S](retVal) } }
извините, мой ответ был специфичен для того, как я использовал этот пост в том, что мне нужно было сделать. Для тех, кто натыкается на этот вопрос, ищем способ найти случай в перечислении, это способ сделать это (новый в Swift 2):
Edit: нижний регистр camelCase теперь является стандартом для значений перечисления Swift 3
// From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case. enum Theme: String { case white, blue, green, lavender, grey } func loadTheme(theme: String) { // this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block if let testTheme = Theme(rawValue: theme) { // testTheme is guaranteed to have an enum value at this point self.someOtherFunction(testTheme) } }
для тех, кто интересуется перечислением в перечислении, ответы, приведенные на этой странице, которые включают статический var / let, содержащий массив всех перечислимые значения являются правильными. Последний пример кода Apple для tvOS содержит точно такую же технику.
это, как говорится, они должны построить более удобный механизм в язык (Apple, вы слушаете?)!
в Swift 3, когда базовое перечисление имеет {rawValue}s,Вы можете реализовать протокол {Strideable}. Преимущества заключаются в том, что никакие массивы значений не создаются, как в некоторых других предложениях, и что стандартный Swift "для i in ..."оператор работает, что делает для хорошего синтаксиса.
// "Int" to get rawValue, and {Strideable} so we can iterate enum MyColorEnum : Int, Strideable { case Red case Green case Blue case Black //-------- required by {Strideable} typealias Stride = Int func advanced(by n:Stride) -> MyColorEnum { var next = self.rawValue + n if next > MyColorEnum.Black.rawValue { next = MyColorEnum.Black.rawValue } return MyColorEnum(rawValue: next)! } func distance(to other: MyColorEnum) -> Int { return other.rawValue - self.rawValue } //-------- just for printing func simpleDescription() -> String { switch self { case .Red: return "Red" case .Green: return "Green" case .Blue: return "Blue" case .Black: return "Black" } } } // this is how you use it: for i in MyColorEnum.Red ... MyColorEnum.Black { print("ENUM: \(i)") }
вы можете попробовать перечислить так
enum Planet: String { case Mercury case Venus case Earth case Mars static var enumerate: [Planet] { var a: [Planet] = [] switch Planet.Mercury { case .Mercury: a.append(.Mercury); fallthrough case .Venus: a.append(.Venus); fallthrough case .Earth: a.append(.Earth); fallthrough case .Mars: a.append(.Mars) } return a } } Planet.enumerate // [Mercury, Venus, Earth, Mars]
Это то, что я в конечном итоге происходит с; я думаю, что он поражает правильный баланс читаемости и ремонтопригодности.
struct Card { // ... static func deck() -> Card[] { var deck = Card[]() for rank in Rank.Ace.toRaw()...Rank.King.toRaw() { for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] { let card = Card(rank: Rank.fromRaw(rank)!, suit: suit) deck.append(card) } } return deck } let deck = Card.deck()
эксперимент был: Эксперимент
добавить метод к карте, которая создает полную колоду карт, с одной картой каждой комбинации ранга и масти.
Итак, без изменения или улучшения данного кода, кроме добавления метода (и без использования материала, который еще не был изучен), я придумал это решение:
struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } func createDeck() -> [Card] { var deck: [Card] = [] for rank in Rank.Ace.rawValue...Rank.King.rawValue { for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue { let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!) //println(card.simpleDescription()) deck += [card] } } return deck } } let threeOfSpades = Card(rank: .Three, suit: .Spades) let threeOfSpadesDescription = threeOfSpades.simpleDescription() let deck = threeOfSpades.createDeck()
как с @Kametrixom ответ здесь Я считаю, что возврат массива будет лучше, чем возврат AnySequence, так как вы можете иметь доступ ко всем лакомствам массива, таким как count и т. д.
вот переписать:
public protocol EnumCollection : Hashable {} extension EnumCollection { public static func allValues() -> [Self] { typealias S = Self let retVal = AnySequence { () -> AnyGenerator<S> in var raw = 0 return AnyGenerator { let current : Self = withUnsafePointer(&raw) { UnsafePointer().memory } guard current.hashValue == raw else { return nil } raw += 1 return current } } return [S](retVal) } }
перечисления имеют методы toRaw() и fromRaw (), поэтому, если ваше необработанное значение является Int, вы можете перебирать от первого до последнего перечисления:
enum Suit: Int { case Spades = 1 case Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } } for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() { if let covertedSuit = Suit.fromRaw(i) { let description = covertedSuit.simpleDescription() } }
один gotcha заключается в том, что вам нужно проверить дополнительные значения перед запуском метода simpleDescription, поэтому мы сначала устанавливаем convertedSuit в наше значение, а затем устанавливаем константу в convertedSuit.simpleDescription ()
Это похоже на хак, но если вы используете сырые значения, вы можете сделать что-то подобное
enum Suit: Int { case Spades = 0, Hearts, Diamonds, Clubs ... } var suitIndex = 0 while var suit = Suit.fromRaw(suitIndex++) { ... }
вот мой предложенный подход. Это не совсем удовлетворительно (я очень новичок в Swift и ООП!) но, может быть, кто-то может уточнить его. Идея состоит в том, чтобы каждое перечисление предоставляло свою собственную информацию о диапазоне as .сначала и.последнее свойство. Он добавляет всего две строки кода к каждому перечислению: все еще немного жестко закодировано, но, по крайней мере, он не дублирует весь набор. Это требует изменения перечисления костюма, чтобы быть Int, как перечисление ранга, а не нетипизированным.
, а не Эхо весь решение, вот код, который я добавил в перечисление ранга, где-то после операторов case (Suit enum аналогичен):
var first: Int { return Ace.toRaw() } var last: Int { return King.toRaw() }
и цикл, который я использовал для построения колоды в виде массива строк. (В определении проблемы не указано, как должна быть структурирована колода.)
func createDeck() -> [String] { var deck: [String] = [] var card: String for r in Rank.Ace.first...Rank.Ace.last { for s in Suit.Hearts.first...Suit.Hearts.last { card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)() deck.append( card) } } return deck }
это неудовлетворительно, потому что свойства связаны с элементом, а не с перечислением. Но это добавляет ясности в циклы "для". Я бы хотел, чтобы он сказал ранг.сначала вместо звания.Туз.первый. Он работает (с любым элементом), но это некрасиво. Может ли кто-нибудь показать, как поднять это до уровня перечисления?
и чтобы заставить его работать, я поднял метод createDeck из структуры карты... не удалось выяснить, как получить массив [String], возвращенный из этой структуры, и это кажется плохим местом для размещения такого метода в любом случае.
Я сделал это с помощью вычисляемого свойства, которое возвращает массив всех значений (благодаря этому сообщению http://natecook.com/blog/2014/10/loopy-random-enum-ideas/). однако он также использует int raw-значения, но мне не нужно повторять все члены перечисления в отдельном свойстве.
обновление Xcode 6.1 немного изменил способ получения члена enum с использованием необработанного значения, поэтому я исправил список. Также исправлена небольшая ошибка с неправильным первым необработанным значением
enum ValidSuits:Int{ case Clubs=0, Spades, Hearts, Diamonds func description()->String{ switch self{ case .Clubs: return "♣︎" case .Spades: return "♠︎" case .Diamonds: return "♦︎" case .Hearts: return "♥︎" } } static var allSuits:[ValidSuits]{ return Array( SequenceOf { () -> GeneratorOf<ValidSuits> in var i=0 return GeneratorOf<ValidSuits>{ return ValidSuits(rawValue: i++) } } ) } }
при общении с
Swift 2.0
вот мое предложение:я добавил тип raw в
Suit
enum
enum Suit: Int {
затем:
struct Card { var rank: Rank var suit: Suit func fullDeck()-> [Card] { var deck = [Card]() for i in Rank.Ace.rawValue...Rank.King.rawValue { for j in Suit.Spades.rawValue...Suit.Clubs.rawValue { deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!)) } } return deck } }
на
enum
представляютInt
enum Filter: Int { case salary case experience case technology case unutilized case unutilizedHV static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case static let allValues = allRawValues.map { Filter(rawValue: )!.rawValue } }
назовем это так:
print(Filter.allValues)
принты:
[0, 1, 2, 3, 4]
на
enum
представляютString
enum Filter: Int { case salary case experience case technology case unutilized case unutilizedHV static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case static let allValues = allRawValues.map { Filter(rawValue: )!.description } } extension Filter: CustomStringConvertible { var description: String { switch self { case .salary: return "Salary" case .experience: return "Experience" case .technology: return "Technology" case .unutilized: return "Unutilized" case .unutilizedHV: return "Unutilized High Value" } } }
называем это
print(Filter.allValues)
принты:
["Зарплата", "Опыт", "Технология", "Неиспользованная", "Неиспользованная Высокая Стоимость"]
другое решение:
enum Suit: String { case spades = "♠" case hearts = "♥" case diamonds = "♦" case clubs = "♣" static var count: Int { return 4 } init(index: Int) { switch index { case 0: self = .spades case 1: self = .hearts case 2: self = .diamonds default: self = .clubs } } } for i in 0..<Suit.count { print(Suit(index: i).rawValue) }
Это довольно старый пост, от Swift 2.0. Теперь здесь есть несколько лучших решений, которые используют новые функции swift 3.0: итерация через перечисление в Swift 3.0
и по этому вопросу есть решение, которое использует новую функцию (еще не выпущенную, когда я пишу это редактирование) Swift 4.2: как мне получить счет быстрого перечисления?
есть много хороших решений в этой теме и других, однако некоторые из них очень сложный. Мне нравится максимально упрощать. Вот решение, которое может или не может работать для разных нужд, но я думаю, что это хорошо работает в большинстве случаев:
enum Number: String { case One case Two case Three case Four case EndIndex func nextCase () -> Number { switch self { case .One: return .Two case .Two: return .Three case .Three: return .Four case .Four: return .EndIndex /* Add all additional cases above */ case .EndIndex: return .EndIndex } } static var allValues: [String] { var array: [String] = Array() var number = Number.One while number != Number.EndIndex { array.append(number.rawValue) number = number.nextCase() } return array } }
выполнить итерации:
for item in Number.allValues { print("number is: \(item)") }
здесь метод, который я использую как для итерации перечисления, так и для предоставления нескольких типов значений из одного перечисления
enum IterateEnum: Int { case Zero case One case Two case Three case Four case Five case Six case Seven //tuple allows multiple values to be derived from the enum case, and //since it is using a switch with no default, if a new case is added, //a compiler error will be returned if it doesn't have a value tuple set var value: (french:String, spanish:String, japanese:String) { switch self { case .Zero: return (french:"zéro", spanish:"cero", japanese:"nuru") case .One: return (french:"un", spanish:"uno", japanese:"ichi") case .Two: return (french:"deux", spanish:"dos", japanese:"ni") case .Three: return (french:"trois", spanish:"tres", japanese:"san") case .Four: return (french:"quatre", spanish:"cuatro", japanese:"shi") case .Five: return (french:"cinq", spanish:"cinco", japanese:"go") case .Six: return (french:"six", spanish:"seis", japanese:"roku") case .Seven: return (french:"sept", spanish:"siete", japanese:"shichi") } } //Used to iterate enum or otherwise access enum case by index order. //Iterate by looping until it returns nil static func item(index:Int) -> IterateEnum? { return IterateEnum.init(rawValue: index) } static func numberFromSpanish(number:String) -> IterateEnum? { return findItem { .value.spanish == number } } //use block to test value property to retrieve the enum case static func findItem(predicate:((_:IterateEnum)->Bool)) -> IterateEnum? { var enumIndex:Int = -1 var enumCase:IterateEnum? //Iterate until item returns nil repeat { enumIndex += 1 enumCase = IterateEnum.item(index: enumIndex) if let eCase = enumCase { if predicate(eCase) { return eCase } } } while enumCase != nil return nil } } var enumIndex:Int = -1 var enumCase:IterateEnum? //Iterate until item returns nil repeat { enumIndex += 1 enumCase = IterateEnum.item(index: enumIndex) if let eCase = enumCase { print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)") } } while enumCase != nil print("Total of \(enumIndex) cases") let number = IterateEnum.numberFromSpanish(number: "siete") print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")
это выход:
число ноль на французском языке: zéro, испанский: cero, японский: nuru
Номер один на французском языке: un, испанский: uno, японский: ichi
Номер два на французском языке: deux, испанский: dos, японский: ni
Число Три на французском языке: trois, испанский: tres, японский: san
Число четыре на французском языке: quatre, испанский: cuatro, японский: Ши
Число пять на французском языке: cinq, испанский: cinco, японский: go
Число шесть на французском: шесть, испанский: seis, японский: roku
Число семь на французском языке: сентябрь, испанский: siete, японский: shichiвсего 8 случаев
siete на японском языке: shichi
обновление
Я недавно создал протокол для перечисления. Протокол требует перечисления с помощью Инт необработанное значение:
protocol EnumIteration { //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil static func item(index:Int) -> Self? static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) { static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? static func count() -> Int } extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int { //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil static func item(index:Int) -> Self? { return Self.init(rawValue: index) } static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) { var enumIndex:Int = -1 var enumCase:Self? //Iterate until item returns nil repeat { enumIndex += 1 enumCase = Self.item(enumIndex) if let eCase = enumCase { item(index: enumIndex, enumCase: eCase) } } while enumCase != nil completion?() } static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? { var enumIndex:Int = -1 var enumCase:Self? //Iterate until item returns nil repeat { enumIndex += 1 enumCase = Self.item(enumIndex) if let eCase = enumCase { if predicate(enumCase:eCase) { return eCase } } } while enumCase != nil return nil } static func count() -> Int { var enumIndex:Int = -1 var enumCase:Self? //Iterate until item returns nil repeat { enumIndex += 1 enumCase = Self.item(enumIndex) } while enumCase != nil //last enumIndex (when enumCase == nil) is equal to the enum count return enumIndex } }
я использовал метод ниже, предположение заключается в том, что я знаю, какое последнее значение в перечислении ранга, и все ранги имеют добавочные значения после Ace
Я предпочитаю этот способ, так как он чистый и небольшой, легко понять
func cardDeck() -> Card[] { var cards: Card[] = [] let minRank = Rank.Ace.toRaw() let maxRank = Rank.King.toRaw() for rank in minRank...maxRank { if var convertedRank: Rank = Rank.fromRaw(rank) { cards.append(Card(rank: convertedRank, suite: Suite.Clubs)) cards.append(Card(rank: convertedRank, suite: Suite.Diamonds)) cards.append(Card(rank: convertedRank, suite: Suite.Hearts)) cards.append(Card(rank: convertedRank, suite: Suite.Spades)) } } return cards }
есть хитрый способ, и разочарование, как это иллюстрирует разницу между двумя разными видами перечислений.
попробуйте это:
func makeDeck() -> Card[] { var deck: Card[] = [] var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades] for i in 1...13 { for suit in suits { deck += Card(rank: Rank.fromRaw(i)!, suit: suit) } } return deck }
дело в том, что перечисление, подкрепленное числами (необработанные значения), неявно явно упорядочено, тогда как перечисление, не подкрепленное числами, явно неявно неупорядочено.
другие языки, которые могут это сделать (итерация по неупорядоченным перечислениям), могут быть теми же языками, где все "под капотом" на самом деле карта или словарь, и вы можете перебирать ключи карты, есть ли какой-либо логический порядок или нет.
Так хитрость заключается в том, чтобы предоставить ему что-то явно упорядоченное, в этом случае экземпляры костюмов в массиве в нужном нам порядке. Как только вы дадите ему это, Свифт похож