Як перерахувати перерахунок з типом String?


530
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

Наприклад, як я можу зробити щось на кшталт:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

Отриманий приклад:

♠
♥
♦
♣

У якому випадку ти б не знав тип?
— mtbennett

Ви праві, в даному випадку це тип String.
— Люсьєн

Жодного роздуму у Свіфті ще ...
— Султан

22
Хіба не іронічно, що їх називають перерахуваннями, але вони так болісно дратують перераховувати у Свіфті?
— Чарлтон Проватас

3
@CharltonProvatas Якби це був єдиний недолік у Swift, я б назвав це за день. Дивлячись на те, скільки людей пропонують різні способи вирішення цього питання, я просто гризу клавіатуру.
— qwerty_so

Відповіді:


267

Швидкий 4.2+

Починаючи з Swift 4.2 (з Xcode 10), просто додайте відповідність протоколу, щоб CaseIterableотримати перевагу allCases. Щоб додати цю відповідність протоколу, вам просто потрібно десь написати:

extension Suit: CaseIterable {}

Якщо перелік є вашим, ви можете вказати відповідність безпосередньо в декларації:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Тоді наступний код надрукує всі можливі значення:

Suit.allCases.forEach {
    print($0.rawValue)
}

Сумісність з більш ранніми версіями Swift (3.x і 4.x)

Якщо вам потрібно підтримати Swift 3.x або 4.0, ви можете імітувати реалізацію 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) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif

@DmitryPetukhov Буду радий допомогти, але: (1) Ви впевнені, що отримали останню версію коду? (деякий збій було виправлено місяць тому) та (2), будь ласка, дайте MCVE вашого власного типу, який може відтворити збій, та вашу версію Xcode.
— Cœur

Для мене це добре працює для нарощування налагодження, але як тільки я створю реліз і завантажую його в TestFlight, він виходить з ладу. Чи Apple якось це викреслює?
— Даніель Вуд

1
Здається, у вашої версії є одна позитивна порівняно з вбудованою версією CaseIterator. Я можу розширити переліки, визначені в іншому файлі з вашою версією. Якщо ви робите allCases у розширенні public, ви також можете розширити переліки, визначені в різних рамках.
— adamfowlerphoto

1
@CyberMew Я оновив відповідь, щоб уточнити її. Книга Swift та згадки про випуск Xcode 10 про CaseIterable - це позиція моєї відповіді, і вони використовували спрощені приклади, коли перерахунок не підкріплений String, на відміну від цього питання щодо переповнення стека.
— Cœur

1
Я хотів би наголосити на важливості швидкості "# if! (> = 4.2)". Якщо ви писали свій код до швидкого 4.2 і ви забули видалити код "нижче якщо! Swift (> = 4.2)", коли ви використовуєте Xcode Версія 11.4 для локальної побудови на вашому тестовому пристрої, все буде добре. Але коли ваш додаток буде завантажено з магазину додатків або тестового польоту, цей фрагмент коду завершить роботу вашого додатка. Такого роду помилку дуже важко помітити чи налагодити.
— Олівер Чжан

524

Ця публікація актуальна тут 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
}

188
Приємно, але ... ви повинні ввести свої елементи перерахунку двічі - один раз для перерахунку, один раз для всіх Значень. Не зовсім такий елегантний, як хотілося б.
— Джей Імерман

2
Погодьтеся з "але" ... однак, як зазначено в статті, можливо, виникає проблема, що перерахунок дійсно є набором і, отже, не упорядкованим ... пам’ятайте, що ... випадки замовлення, визначені в, не були б поганим початком!
— rougeExciter

3
У Java компілятор робить це для вас, можливо, Swift 2.0 зробить це також. Зокрема, у Java всі переліки отримують метод опису (toString в Java), який дає String як назви випадків (Шайби, ...), а набір справ створюється автоматично. Java також дає позиційну індексацію. Як я вже говорив, можливо, Swift 2.0.
— Говард Ловатт

1
В ідеалі у вас є щось схоже на реалізацію c #, в якій ви можете це робити, Enum.Values(typeof(FooEnum))але викриваєтесь як метод розширення (наприклад, карта чи зменшення). FooEnum.values() :: values(EnumType -> [EnumType])
— rodrigoelp

1
Стаття дає змогу зробити висновок про те, що кожне значення перерахунку знаходиться у масиві allValues ​​з одиничним тестом. Однак я все ще можу побачити, як хтось додає більше елементів, але не застосовує їх у тесті одиниць, який все ще залишає нас на початку, не знаючи точно, що всі значення перерахунків зберігаються у всіх значеннях.
— DonnaLea

278

Я зробив функцію корисності 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: $0), T.self) }
        case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
        case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
        case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
        default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

Основна ідея:

  • Представлення пам’яті enum, за винятком enums з пов’язаними типами, - це лише індекс випадків, коли кількість випадків є 2...256, вона ідентична UInt8коли 257...65536, це UInt16так і так далі. Отже, це може бути unsafeBitcastз відповідних непідписаних цілих типів.
  • .hashValue значень enum - це те саме, що і індекс справи.
  • .hashValueзначень enum, що передаються з недійсного індексу, є 0.

Переглянуто для Swift2 та реалізувало ідеї кастингу з відповіді @ Kametrixom :

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return anyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer<T>($0).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) {
            $0.withMemoryRebound(to: T.self, capacity: 1) { $0.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) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

20
Дивовижна і єдина відповідь, яка відповідає на питання! Але так ... не торкаюся! Хоча +1 за зусилля!
— dotToString

Я щойно опублікував свою відповідь, яка працює в основному так само (цю відповідь побачила лише пізніше). Він використовує Swift 2.0 beta 6 та сучасні особливості мови.
— Kametrixom

2
Версія Swift 3 працює добре. Просто потрібно трохи змінити використання: for f in iterateEnum (Suit.self) {print (f.rawValue)}
— Gene De Lisa

16
+1 це досить геніально. Крім того, IMHO занадто розумний у використанні, про що свідчить, що він суттєво ламається при кожній важливій зміні версії Swift. На відміну від автора, версія Swift 3 була зроблена за місяць до виходу бета-версії Swift 3 ... Якщо ви збираєтесь взяти цю відповідь і дізнатися все це withUnsafePointer withMemoryReboundта pointeeінше, то використовуйте це будь-якими способами. Інакше я б цього уникав.
— Дан Розенстарк

5
Я просто хочу додати, що це зараз зламано у швидкому 4, але лише на Linux, тому +1 до вищезазначених коментарів, що це занадто розумно, щоб використовувати.
— Ендрю Пламмер

130

Інші рішення спрацьовують, але всі вони роблять припущення, наприклад, про кількість можливих чинів та костюмів, або про те, яким може бути перший і останній ранг. Правда, макет колоди карт, мабуть, не зміниться в найближчому майбутньому. Однак, як правило, написання коду більш акуратно, що дозволяє робити якомога менше припущень. Моє рішення:

Я додав необроблений тип до Suitenum, тому я можу використовувати Suit(rawValue:)для доступу до Suitсправ:

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:)є недоступним ініціалізатором і повертає необов'язково. Розкручуючи та перевіряючи його значення в обох під час операторів, не потрібно вважати кількість Rankчи Suitвипадки:

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

12
Абсолютна НАЙКРАЩА відповідь, яку я бачив у різних темах на цю тему. Дуже елегантний. Це працює з перерахунками типу Int, але мені цікаво, як можна перебирати інші типи (рядок, користувацькі типи тощо).
— Джей Імерман

3
Це, безумовно, найкраще рішення. Одне зауваження. У прикладі в книзі він не має "case Spades = 1", як має sdduursma. Я спочатку цього не впіймав. це один варіант, або ви можете просто скористатися "var m = 0"
— Джошуа Гуссен

10
Це робить припущення, що необроблені значення є послідовними. Якщо це не відповідає дійсності, наприклад, коли enum являє прапори бітової маски, цикл виходить передчасно.
— Джим

1
Це рішення передбачає, що ви можете змінити визначення Suit. Ви можете в цьому прикладі, але вправа мала на меті змусити вас працювати з enumsвами, як вони були із зовнішнього джерела.
— Джош J

1
Єдине, на що я хочу поскаржитися, це те, що я не можу назвати це статичним методом, але спершу потрібно створити об’єкт картки.
— qed

76

Я спіткнувся в бітах і байтах і створив розширення, за яким пізніше я виявив, що роботи дуже схожі на відповідь @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($0).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) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

Я поняття не маю, навіщо мені це потрібно typealias, але компілятор скаржиться без цього.


10
Ця відповідь навіть краща за мою відповідь, особливо в частині кастингу :)
— rintaro

Але я думаю, що це працює лише на маленьких ендіанських умовах?
— rintaro

5
Xcode 8 beta 6 змінив це знову! Я отримую таку помилку, що "init" недоступний: використовувати "withMemoryRebound (до: місткість: _)", щоб тимчасово переглянути пам'ять як інший тип, сумісний з компонуванням. "
— Confused Vorlon

1
@ConfusedVorlon: див. Відповідь вище від @Rintaro: заміни withUnsafePointer… pointee}відwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
— Stefan

1
Здається, це вже не працює з Xcode 10. (я знаю, що це не потрібно для Swift 4.2), але при використанні Swift 4 (.1) у Xcode 10 цей код більше не працює (
— вихідне

26

Ви можете повторити перерахунок, застосувавши ForwardIndexTypeпротокол.

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
}

2
Я вважаю це найбільш "правильною" відповіддю на питання, навіть найелегантнішою (потрібний додатковий код стосовно інших варіантів, що витримуються тут), враховуючи результуючий синтаксис при використанні в межах діапазону (синтаксис - це те, що я сподівався б, що зможе це зробити, якщо перераховує, де перераховується обхідне число). Дякую! Хоча варто зазначити, що якщо перевантаження оператора використовується в іншому місці, крім наступника () (що здається спокусливим), очевидно, примусове розкручування є небезпечним. Також інфікс здається непотрібним ...?
— iOS Gamer

Оновлена ​​відповідь, щоб відобразити останні специфікації мови Swift
— RndmTsk

Правильно визначений successor()метод (перший варіант) позбавив би потреби enumмати асоційований тип. +1
— nhgrif

1
Але ця елегантна відповідь не спрацює для струнних перерахунків, чи не так?
— Алі

Найбільш "правильне" / найкраще рішення! + 1-ед
— Сіу Чінг Понг -Асука Кенджі-

18

Зараз ця проблема набагато простіша. Ось моє рішення Swift 4.2:

enum Suit: Int, CaseIterable {
  case None
  case Spade, Heart, Diamond, Club

  static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}

enum Rank: Int, CaseIterable {
  case Joker
  case Two, Three, Four, Five, Six, Seven, Eight
  case Nine, Ten, Jack, Queen, King, Ace

  static let allNonNullCases = Rank.allCases[Two.rawValue...]
}

func makeDeck(withJoker: Bool = false) -> [Card] {
  var deck = [Card]()
  for suit in Suit.allNonNullCases {
    for rank in Rank.allNonNullCases {
      deck.append(Card(suit: suit, rank: rank))
    }
  }
  if withJoker {
    deck.append(Card(suit: .None, rank: .Joker))
  }
  return deck
}

Попередньо 4.2:

Мені подобається це рішення, яке я зібрав після пошуку " Списку розуміння у Швидкому ".

Він використовує Int raws замість Strings, але дозволяє уникнути введення двічі, він дозволяє налаштувати діапазони, а також не важко кодує необроблені значення.

Це версія мого оригінального рішення Swift 4, але див. Покращення 4.2 вище:

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: $0)! })
}
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: $0)! })
}
func makeDeck(withJoker: Bool = false) -> [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
}

О, я бачу, що моя в основному така ж, як і у Sutean Rutjanalard.
— adazacom

1
Насправді мені більше сподобалась ваша реалізація. Я думаю, це зрозуміліше! 1 внесок. Насправді найбільш голосовані відповіді занадто розумні і, безумовно, будуть порушені в майбутньому. Ваш обіцяє певну стабільність у майбутньому.
— jvarela

17

У принципі це можна зробити так, припускаючи, що ви не використовуєте призначення необроблених значень для випадків enum:

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())")
}

7
Це приємно, але воно працює лише для безперервних перерахунків на Integer, починаючи з 0
— Роберт

@Robert, як говориться в моєму коментарі вище: "Ви не використовуєте призначення присвоєних значень для випадків перерахунків"
— Alfa07

Так - не використовуйте необроблені значення, а також не встановлюйте базовий тип як int. Швидко вам не потрібен тип для перерахунків, як у прикладі костюмів. enum ItWontWorkForThisEnum {case a, b, c}
— Роберт

Як би вирішити цю проблему, якщо кортеж пов'язаний із справою перерахування?
— rougeExciter

Ви не можете дуже легко пов’язати кортеж із перерахунком.
— nhgrif

13

Якщо ви даєте enum сирого значення 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 }
}

Тепер, коли ви створюєте enum (доки він має значення 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())

Остаточний клас Suit, включаючи заміну 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)
}

Синтаксис 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
    }
}

NEXTINDEX ++ треба увійти на сайт стриже 3. Що ви пропонуєте в якості заміни - вар NEXTINDEX = firstRawValue () повертає anyGenerator {Самого (RawValue: NEXTINDEX ++)}
— Avi

Зрозумів це. Defer {NEXTINDEX + = 1} {повернення AnyGenerator себе (RawValue: NEXTINDEX)}
— Аві

12

Оновлено до Swift 2.2 +

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

Це оновлений код до форми Swift 2.2 @ відповіді Kametrixom

Для Swift 3.0+ (велике спасибі @Philip )

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).pointee
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

@silvansky, чи можете ви поясніть, що ви маєте на увазі?
— ale_stro

На жаль, я повторно перевірив і з’явилася помилка на дитячому майданчику: у реальному проекті цей код працює як очікувалося, дякую! =)
— сильванський

1
Чудове рішення! Також відмінно працює на Swift 3 з мінімальними змінами ("AnyGenerator" перейменовано на "AnyIterator" і ".memory" перейменовано на ".pointee").
— Філіп

9

Рішення Swift 5:

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}

7

Я виявив, що роблю .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) {
                $0.withMemoryRebound(to: T.self, capacity: 1) { $0.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({$0.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"]

7

Xcode 10 за допомогою Swift 4.2

enum Filter: String, CaseIterable {

    case salary = "Salary"
    case experience = "Experience"
    case technology = "Technology"
    case unutilized = "Unutilized"
    case unutilizedHV = "Unutilized High Value"

    static let allValues = Filter.allCases.map { $0.rawValue }
}

Назви це

print(Filter.allValues)

Друкує:

["Зарплата", "Досвід", "Технологія", "Не використана", "Невикористана висока цінність"]


Старіші версії

За 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: $0)!.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: $0)!.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)

Друкує:

["Зарплата", "Досвід", "Технологія", "Не використана", "Невикористана висока цінність"]


7

EDIT: пропозиція швидкої еволюції SE-0194 Отримана колекція випадків Enum пропонує рівне рішення цієї проблеми. Ми бачимо це у Swift 4.2 та новіших версіях. Пропозиція також вказує на деякі обхідні шляхи , подібні до вже згаданих тут, але це може бути цікаво все-таки побачити.

Я також збережу свій оригінальний пост для повноти.


Це ще один підхід, заснований на відповіді @ Пейманха, адаптованому до 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) {
                 $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
            }
            guard current.hashValue == raw else { return nil }
            raw += 1
            return current
        }
    }

    return [S](retVal)
}

5
enum Rank: Int {
    ...
    static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }

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

Як щодо цього?


Дякую, працює як треба. Але чи є можливість у закритті карти отримати значення не за індексом, а за назвою?
— Михайло

4

Можна спробувати перерахувати так

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]

1
Це багато марного коду! Це еквівалентно static var enumerate = [Mercury, Venus, Earth, Mars], роблячи це відповіді підрозділу порівняно з найбільш голосовою відповіддю stackoverflow.com/a/24137319/1033581
— Cœur

@ Cœur ця відповідь має важливу перевагу використання компілятора, щоб гарантувати, що ви не пропустите справу.
— дчакаров

@ Cœur з тією ж проблемою, що дозволяє вам зробити помилку користувача, тобто компілятор не поскаржиться, якщо ви return [Mercury, Venus, Mars]return [Mercury, Venus, Earth, Mars]
— напишете

@dchakarov Я вирішив опублікувати покращення як відповідь, для наочності: stackoverflow.com/a/50409525/1033581
— Cœur

@ Cœur Якщо у вашій новій відповіді ви заміните оператор return на це, return [.spades, .hearts, .clubs]компілятор нічого не скаже, і тоді, коли ви спробуєте використовувати його в коді, ви отримаєте [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- це було моє значення - що якщо ви маєте справу з великим enum, і вам доведеться час від часу додавати або видаляти справи, ваше рішення схильне до помилок упущення, тоді як поточна відповідь, хоча і не стисла, безпечніша.
— дчакаров

4

У Swift 3, коли є базовий перелік rawValue, ви можете реалізувати Strideableпротокол. Переваги полягають у тому, що не створюються масиви значень, як у деяких інших пропозиціях, і те, що стандартний цикл Swift "for 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)")
}

Ах, саме те, що я шукав, щоб замінити ForwardIndexType. Тепер мої ітерації добре виглядають на сайті користування ... просто належним швидким способом.
— Ендрю Дункан

4

Це рішення відповідає правильному балансу читабельності та ремонтопридатності.

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

На мою думку, це найкраще рішення. Коли я бачу швидкий код, в основному, читабельність не краща за objc. Але це може бути, якби програмісти приділяли більшу увагу читачам свого коду. Наприклад, їх майбутнє:
— Вілем Курц

4

Вибачте, моя відповідь була специфічною для того, як я використовував цю посаду в тому, що мені потрібно було зробити. Для тих, хто натрапляє на це питання, шукаючи спосіб знайти справу у перерахунку, це такий спосіб зробити (новий у Swift 2):

Редагувати: низький регістр 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)
        }
    }

Для тих, хто цікавиться перерахуванням на enum, відповіді, наведені на цій сторінці, що містять статичний var / let, що містить масив усіх значень enum, є правильними. Останній код прикладу Apple для tvOS містить цю саму методику.

Попри це, вони повинні побудувати більш зручний механізм у мові (Apple, ви слухаєте?)!


3

Експеримент був: ДОСВІД

Додайте метод до Картки, який створює повну колоду карт, по одній картці кожної комбінації з рангу та масті.

Таким чином, не змінюючи і не вдосконалюючи даний код, окрім додавання методу (і без використання матеріалів, які ще не вивчені), я придумав таке рішення:

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

3

Ось метод, який я використовую для ітерації enumта надання декількох типів значень з одногоenum

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 { $0.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, японська: shi
Число п'ять французькою мовою: cinq, іспанська: cinco, японська: go
Номер шість французькою: шість, іспанська: seis, японський: roku
Кількість Сім французькою мовою: sept, іспанська: siete, японська: shichi

Всього 8 справ

siete по-японськи: shichi


ОНОВЛЕННЯ

Нещодавно я створив протокол для обробки перерахунку. Протокол вимагає перерахунку із значенням Int raw:

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

2

Це здається злому, але якщо ви використовуєте необроблені значення, ви можете зробити щось подібне

enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  

2

Маючи справу з Swift 2.0ось моя пропозиція:

Я додав "необроблений" тип 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
    }
}

2

Як і у відповіді на @Kametrixom, тут я вважаю, що повернення масиву було б краще, ніж повернення AnySequence, оскільки ви можете мати доступ до всіх приємностей Array, таких як 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($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }

        return [S](retVal)
    }
}

2

Ще одне рішення:

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

2

Це досить стара публікація від Swift 2.0. Зараз є кілька кращих рішень, які використовують новіші функції swift 3.0: Ітерація через Enum у Swift 3.0

І в цьому питанні є рішення, яке використовує нову функцію (ще не випущена, коли я пишу цю редагування) Swift 4.2: Як отримати рахунок підрахунку Swift?


У цій темі є багато хороших рішень та інші, проте деякі з них дуже складні. Мені подобається максимально спрощувати. Ось рішення, яке може працювати або не працювати для різних потреб, але я думаю, що воно працює в більшості випадків:

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

1
Це відчуває як велика робота, яка є специфічною для індивідуального переліку, який ви створили - я не впевнений, що повернення [Number.One.rawValue, Number.Two.rawValue, ...] в цьому випадку не є чистішим. .
— Скотт Остін

Це досить стара публікація від Swift 2.0. Зараз є кілька кращих рішень, які використовують новіші функції swift 3.0: stackoverflow.com/questions/41352594/… І в цьому питанні є рішення, яке використовує нову функцію (ще не випущена, коли я пишу цю редагування ) Swift 4.2: stackoverflow.com/questions/27094878/…
— Аббатство Джексон

2

Енуми мають toRaw()і fromRaw()методи. Отже, якщо ваше неоцінене значення - це Int, ви можете повторювати від першого до останнього enum:

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

Один з моментів полягає в тому, що вам потрібно перевірити наявність необов'язкових значень перед запуском simpleDescriptionметоду, тому ми convertedSuitспочатку встановимо наше значення, а потім встановимо константу наconvertedSuit.simpleDescription()


2
Оригінальне запитання
— стосувалося переліку

2

Ось мій запропонований підхід. Це не зовсім задовільно (я дуже новачок у Swift та OOP!), Але, можливо, хтось може це вдосконалити. Ідея полягає у тому, щоб кожен перерахунок надав власну інформацію про діапазон як .firstта .lastвластивості. Він додає лише два рядки коду до кожного перерахунку: все ще трохи жорстко закодований, але принаймні це не дублювання всього набору. Це вимагає, щоб модифікація Suitenum була Int, такою, якою Rankє enum, а не типізованою.

Замість того, щоб повторювати все рішення, ось код, який я додав до .enum, десь після випадок справи ( Suitenum схожий):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

і цикл, який я використовував для створення колоди як масиву String. (У визначенні проблеми не вказано, як колоду слід структурувати.)

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
}

Це незадовільно, оскільки властивості пов'язані не з елементом, а з елементом. Але це додає чіткості до циклів 'for'. Я хотів би сказати Rank.firstзамість цього Rank.Ace.first. Це працює (з будь-яким елементом), але це некрасиво. Чи може хтось показати, як підняти це до рівня перерахунків?

І щоб він працював, я createDeckвийняв метод із структури Карт. Я не міг зрозуміти, як отримати масив [String], повернутий із цієї структури, і це здається поганим місцем для того, щоб все-таки поставити такий метод.


2

Я зробив це за допомогою обчисленої властивості, яка повертає масив усіх значень (завдяки цій публікації http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Однак він також використовує int raw-значення, але мені не потрібно повторювати всі члени перерахування в окремій властивості.

UPDATE Xcode 6.1 трохи змінив спосіб отримання членами rawValueпереліку, тому я виправив список. Виправлена ​​також невелика помилка спочатку неправильно rawValue.

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

1
enum Rank: Int
{
    case Ace = 0
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    case Count
}

enum Suit : Int
{
    case Spades = 0
    case Hearts, Diamonds, Clubs
    case Count
}

struct Card
{
    var rank:Rank
    var suit:Suit
}

class Test
{
    func makeDeck() -> Card[]
    {
        let suitsCount:Int = Suit.Count.toRaw()
        let rankCount:Int = Rank.Count.toRaw()
        let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
        let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)

        for i:Int in 0..rankCount
        {
            for j:Int in 0..suitsCount
            {
                deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
            }
        }
        return deck
    }
}

На основі відповіді Ріка: це в 5 разів швидше


Додавання Countсправи порушить вичерпні switchреалізації та CaseIterableвідповідність.
— Cœur
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.