Як перерахувати перерахунок з типом 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: заміни withUnsafePointerpointee}від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.