Как перечислить перечисление со строковым типом?

486
enum Suit: String {
    case spades = ""
    case hearts = ""
    case diamonds = ""
    case clubs = ""
}

Например, как я могу сделать что-то вроде:

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

Результирующий пример:





  • 0
    В каком случае вы не знаете тип?
  • 0
    Вы правы, в данном случае это тип String.
Показать ещё 5 комментариев
Теги:
string
enums
enumerate

42 ответа

149
Лучший ответ

Свифт 4. 2+

Начиная с Swift 4.2 (с Xcode 10), просто добавьте соответствие протокола в CaseIterable чтобы извлечь выгоду из всех allCases:

extension Suit: CaseIterable {}

Тогда это напечатает все возможные значения:

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

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

Просто имитируйте реализацию Swift 4.2:

#if !swift(>=4.2)
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            var first: Self?
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif
  • 1
    Спасибо за информацию! Прямо сейчас подойдет «мимический» вариант.
  • 0
    @DmitryPetukhov Я был бы рад помочь, но: (1) вы уверены, что получили последнюю версию кода? (некоторый сбой был исправлен месяц назад) и (2), пожалуйста, укажите MCVE вашего пользовательского типа, который может воспроизвести сбой, и вашу версию Xcode.
Показать ещё 7 комментариев
503

Этот пост актуален здесь 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
}
  • 179
    Хорошо, но ... вы должны ввести свои элементы перечисления дважды - один раз для перечисления, один раз для allValues. Не так элегантно, как хотелось бы.
  • 2
    Согласитесь с «но» ... однако, как указано в статье, возможно, существует проблема, заключающаяся в том, что перечисление действительно является множеством и, следовательно, неупорядоченным ... обратите внимание ... случаи, определенные в, не будут плохим началом!
Показать ещё 6 комментариев
259

Я сделал служебную функцию 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 - исключение enum со связанными типами - это всего лишь индекс случаев, когда количество случаев 2...256, оно идентично UInt8, когда 257...65536, оно UInt16 и т.д. Таким образом, это может быть unsafeBitcast из соответствующих неподписанных целых типов.
  • .hashValue значений перечисления совпадает с индексом case.
  • .hashValue значений перечисления, переданных из недопустимого индекса, 0

ДОБАВЛЕНО:

Пересмотрено для Swift2 и реализовано идеи литья из @Kametrixom answer

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
    }
}
  • 18
    Удивительный и единственный ответ, который отвечает на вопрос! Но да ... не буду трогать это! +1 за усилие!
  • 0
    Я только что опубликовал свой ответ, который работает в основном таким же образом (только позже увидел этот ответ). Он использует Swift 2.0 beta 6 и современные функции языка.
Показать ещё 13 комментариев
132

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

Я добавил необработанный тип в переименование костюма, поэтому я могу использовать 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)
        }
    }
}

Ниже реализации метода card createDeck(). init (rawValue:) является отказоустойчивым инициализатором и возвращает необязательный. Развертывая и проверяя его значение в обоих заявлениях, нет необходимости предполагать количество рангов или случаев:

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, но мне интересно, как можно перебирать другие типы (строки, пользовательские типы и т. Д.).
  • 0
    Одна проблема с этим: я получил синтаксическую ошибку, используя обозначение «+ =». «Не удалось выполнить игровую площадку: / var / folder / ... expr.AEK8PX.swift: ...: error:« [(Card)] »не идентична колоде« UInt8 »+ = Card (ранг: ранг, масть: suit) ^ "Использование deck.append () вместо deck + = работало нормально. Это было в Xcode 6b5 - возможно, ошибка.
Показать ещё 11 комментариев
71

Второй ответ, который действительно работает

Поэтому я наткнулся на биты и байты и создал расширение (которое позже я обнаружил, что оно очень похоже на ответ @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 не имею, зачем мне typealias, но компилятор жалуется без этого)

(Я сделал большую модификацию этого ответа, посмотрите правки прошлых версий)

  • 9
    Этот ответ даже лучше моего ответа, особенно в кастинге :)
  • 0
    Но я думаю, что это работает только на байтовой среде?
Показать ещё 18 комментариев
24

Вы можете выполнить итерацию через перечисление путем реализации протокола 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
    Я считаю, что это самый «правильный» ответ на вопрос, даже самый «элегантный» (дополнительный код, необходимый для противостояния другим опциям здесь), учитывая результирующий синтаксис при использовании в диапазоне (синтаксис - это то, что я можно было бы ожидать, если бы перечисления, где перечислимых без обходных путей). Спасибо! Хотя стоит отметить, что если перегрузка оператора используется в другом месте, кроме функции successor () (что кажется заманчивым), то очевидно, что принудительное развертывание опасно. Кроме того, инфикс кажется ненужным ...?
  • 0
    Обновленный ответ, отражающий последние спецификации языка Swift
Показать ещё 4 комментария
17

В принципе это можно сделать так, предполагая, что вы не используете назначение исходных значений для перечислений:

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
    Это хорошо, но это работает только для непрерывных целых чисел, начинающихся с 0
  • 0
    @ Роберт, как сказано в моем комментарии выше: «Вы не используете необработанные присваивания значений для случаев enum»
Показать ещё 4 комментария
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 }
}

Теперь, когда вы создаете перечисление (пока оно имеет значение 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() { ... }

Если значения enum не начинаются с 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)
}

EDIT:

Swift 3:

protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstRawValue() -> Int
}

extension EnumerableEnum {
    static func enumerate() -> AnyIterator<Self> {
        var nextIndex = firstRawValue()

        let iterator: AnyIterator<Self> = AnyIterator {
            defer { nextIndex = nextIndex + 1 }
            return Self(rawValue: nextIndex)
        }

        return iterator
    }

    static func firstRawValue() -> Int {
        return 0
    }
}
  • 0
    nextIndex ++ будет удалено в swift 3. Что вы предлагаете в качестве замены - var nextIndex = firstRawValue () return anyGenerator {Self (rawValue: nextIndex ++)}
  • 0
    Догадаться. defer {nextIndex + = 1} return AnyGenerator {Self (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 a swer

Для 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
        }
    }
}
  • 0
    @silvansky не могли бы вы объяснить, что вы имеете в виду?
  • 0
    Ой, извините, я перепроверил, и была ошибка на игровой площадке: в реальном проекте этот код работает как положено, спасибо! знак равно
Показать ещё 1 комментарий
11

Эта проблема теперь намного проще. Вот мое решение 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

Мне нравится это решение, которое я собрал после нахождения этой страницы: Понимание списка в Swift

Он использует Int raw вместо 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
}
  • 0
    О, теперь я вижу, что мой в основном тот же, что и у Сутеана Рутжаналарда.
  • 1
    На самом деле, мне понравилась ваша реализация лучше. Я думаю, что это понятнее! 1 ответ. На самом деле большинство проголосовавших ответов слишком умны и наверняка сломаются в будущем. Ваш обещает некоторую стабильность в будущем.
Показать ещё 1 комментарий
7

Я обнаружил, что делаю .allValues alot по всему моему коду. Я наконец понял способ просто соответствовать протоколу 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"]
6

РЕДАКТИРОВАТЬ: Swift Evolution Proposal SE-0194 Производная коллекция Enum Case предлагает поэтапное решение этой проблемы. Мы видим это в Swift 4.2 и новее. В предложении также указываются некоторые обходные пути, аналогичные тем, которые уже упоминались здесь, но, тем не менее, было бы интересно посмотреть.

Я также сохраню свой оригинальный пост для полноты картины.


Это еще один подход, основанный на ответе @Peymmankh, адаптированный к Swift 3.

public protocol EnumCollection : Hashable {}

extension EnumCollection {

public static func allValues() -> [Self] {
    typealias S = Self

    let retVal = AnySequence { () -> AnyIterator<S> in
        var raw = 0
        return AnyIterator {
            let current = withUnsafePointer(to: &raw) {
                 $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
    }
}

Как насчет этого?

  • 0
    Спасибо, все работает как мне нужно. Но есть ли возможность в замыкании карты получить значение не по индексу, а по имени?
  • 1
    Дубликат наиболее проголосовавшего ответа stackoverflow.com/a/24137319/1033581
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
  • 0
    @ Cœur этот ответ имеет важное преимущество использования компилятора, чтобы гарантировать, что вы не пропустите дело.
Показать ещё 3 комментария
4

В Swift 3, когда базовое перечисление имеет {rawValue} s, вы можете реализовать протокол {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)")
}
  • 0
    Ааа, именно то, что я искал, чтобы заменить ForwardIndexType. Теперь мои итерации выглядят хорошо на сайте использования ... просто надлежащим образом Swifty.
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 an optional, which is why we introduce the if/let block
    if let testTheme = Theme(rawValue: theme)
        {
        // testTheme is guaranteed to have an enum value at this point
        self.someOtherFunction(testTheme)
        }
    }

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

Говоря это, они должны создать более удобный механизм в языке (Apple, вы слушаете?)!

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()
  • 0
    На мой взгляд, это лучшее решение. Когда я вижу быстрый код, в основном читаемость не лучше, чем у objc. Но могло бы быть, если бы программисты уделяли больше внимания читателям своего кода. Их будущие я, например :)
3

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)

Печать:

["Заработная плата", "Опыт", "Технология", "Неиспользованный", "Неиспользованная высокая стоимость"]

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

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

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

Это вывод:

Число ноль на французском: ноль, испанский: церо, японский: нуру
Номер один на французском: un, испанский: uno, японский: ichi
Номер два на французском: deux, испанский: dos, японский: ni
Номер три по-французски: trois, испанский: tres, японский: san
Номер четыре на французском: quatre, испанский: cuatro, японский: shi
Номер пять на французском: cinq, испанский: cinco, японский: go
Номер шесть по-французски: шесть, испанский: seis, японский: roku
Число семь на французском: сентябрь, испанский: сите, японский: шичи

Всего 8 случаев

сито по японски shichi


ОБНОВИТЬ

Недавно я создал протокол для обработки перечисления. Протокол требует перечисления с необработанным значением Int:

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

Как и с @Kametrixom answer здесь Я считаю, что возвращение массива было бы лучше, чем возвращение 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)
    }
}
  • 0
    Это правильное решение.
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
    Это похоже на большую работу, специфичную для каждого созданного вами перечисления - я не уверен, что return [Number.One.rawValue, Number.Two.rawValue, ...] не чище, в этом случае ,
  • 0
    Это довольно старый пост из Swift 2.0. В настоящее время здесь есть несколько лучших решений, в которых используются более новые функции swift 3.0: stackoverflow.com/questions/41352594/… И в этом вопросе есть решение, в котором используется новая функция (еще не выпущенная, когда я пишу эту редакцию ) Swift 4.2: stackoverflow.com/questions/27094878/…
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

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

UPDATE Xcode 6.1 немного изменил способ получения члена перечисления с использованием исходного значения, поэтому я фиксировал листинг. Также исправлена ​​небольшая ошибка с неправильным первым исходным значением

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

Вот мой предложенный подход. Это не совсем удовлетворительно (я очень новичок в Swift и OOP!), Но, возможно, кто-то может его усовершенствовать. Идея состоит в том, чтобы каждый enum предоставлял свою собственную информацию диапазона как .first и .last. Он добавляет только две строки кода для каждого перечисления: все еще немного жестко закодирован, но по крайней мере он не дублирует весь набор. Это требует модификации перечисления Suit как Int, как и перечисление Rank, вместо нетипизированного.

Вместо того, чтобы повторять все решение, здесь код, который я добавил в перечисление Rank, где-то после операторов case (Suit enum аналогичен):

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
}

Это неудовлетворительно, потому что свойства связаны с элементом, а не с перечислением. Но это добавляет ясности в петли "для". Я хотел бы, чтобы он сказал Rank.first вместо Rank.Ace.first. Он работает (с любым элементом), но он уродлив. Может ли кто-нибудь показать, как повысить это до уровня перечисления?

И чтобы он работал, я снял метод createDeck из структуры Card... не мог понять, как получить массив [String], возвращенный из этой структуры, и это кажется плохим местом для размещения такого метода в любом случае.

2

Это похоже на хак, но если вы используете исходные значения, вы можете сделать что-то вроде этого

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

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

У перечислений есть методы toRaw() и fromRaw(), поэтому, если ваше исходное значение является Int, вы можете перебирать от первого до последнего перечисления:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}

for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
    if let covertedSuit = Suit.fromRaw(i) {
        let description = covertedSuit.simpleDescription()
    }
}

Один из них заключается в том, что перед запуском метода simpleDescription вам нужно проверить дополнительные значения, поэтому сначала установите значение convertSuit в наше значение, а затем установите константу в convertSuit.simpleDescription()

  • 2
    Первоначальный вопрос был о перечислении типа String, а не Int
1

(Улучшение Karthik Кумар ответ)

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

enum Suit: String {
    case spades = ""
    case hearts = ""
    case diamonds = ""
    case clubs = ""

    static var enumerate: [Suit] {
        switch Suit.spades {
        // make sure the two lines are identical ^_^
        case        .spades, .hearts, .diamonds, .clubs:
            return [.spades, .hearts, .diamonds, .clubs]
        }
    }
}
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 раз быстрее

  • 0
    Добавление Count случая сломается исчерпывающий switch реализация и CaseIterable соответствия.
1

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

Попробуйте следующее:

    func makeDeck() -> Card[] {
      var deck: Card[] = []
      var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
      for i in 1...13 {
        for suit in suits {
          deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
        }
      }
      return deck
    }

Сделка заключается в том, что перечисление, поддерживаемое числами (необработанные значения), явно не упорядочено, тогда как перечисление, которое не поддерживается числами, явно неявно неупорядочено.

например. когда мы даем числа значений перечисления, язык достаточно хитер, чтобы выяснить, в каком порядке находятся числа. Если, с другой стороны, мы не даем никаких заказов, когда мы пытаемся перебирать значения, которые язык поднимает вверх в воздухе, и идет "да, но какой из них вы хотите сделать первым?"

Другие языки, которые могут это сделать (итерация по неупорядоченным перечислениям), могут быть теми же языками, где все "под капотом" фактически является картой или словарем, и вы можете перебирать ключи карты, есть ли логическое упорядочение или нет.

Итак, трюк заключается в том, чтобы предоставить ему что-то явно упорядоченное, в данном случае экземпляры костюмов в массиве в том порядке, в котором мы хотим. Как только вы это дадите, Свифт похож на "ну, почему вы не сказали это в первую очередь?"

Другой сокращенный трюк - использовать оператор принуждения для функции fromRaw. Это иллюстрирует еще одну "готовность" к перечислениям, что диапазон возможных значений, которые необходимо пройти, часто больше, чем диапазон перечислений. Например, если бы мы сказали Rank.fromRaw(60), то не было бы возвращенного значения, поэтому мы используем необязательную функцию языка, и где мы начнем использовать опции, форсирование скоро последует, (Или поочередно, если пусть конструкция, которая все еще кажется немного странной для меня)

  • 0
    Смотрите также ответы Джона и Эндрю.
  • 1
    «Дело в том, что перечисление, подкрепленное числами (необработанными значениями), неявно явно упорядочено, тогда как перечисление, которое не подкреплено числами, явно неявно неупорядочено». -- Я не согласен. «Естественный» порядок каждого перечисления (независимо от того, поддерживается ли он целыми числами) должен соответствовать порядку, в котором объявлены элементы перечисления. Обычно это в любом случае совпадает с порядком необработанных значений для целочисленных значений, но это не требуется.
Показать ещё 1 комментарий
1

Я использовал метод ниже, предположение состоит в том, что я знаю, что является последним значением в перечислении Rank, и все ранги имеют инкрементные значения после Ace

Я предпочитаю этот путь, поскольку он чист и мал, легко понять

 func cardDeck() -> Card[] {
    var cards: Card[] = []
    let minRank = Rank.Ace.toRaw()
    let maxRank = Rank.King.toRaw()

    for rank in minRank...maxRank {
        if var convertedRank: Rank = Rank.fromRaw(rank) {
            cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
            cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
            cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
            cards.append(Card(rank: convertedRank, suite: Suite.Spades))
        }
    }

    return cards
}
0

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

В принципе, вместо использования перечисления, создайте структуру с одним экземпляром и сделайте все константы enum-values. Затем переменные могут быть запрошены с помощью Mirror

public struct Suit{

    // the values
    let spades = ""
    let hearts = ""
    let diamonds = ""
    let clubs = ""

    // make a single instance of the Suit struct, Suit.instance
    struct SStruct{static var instance: Suit = Suit()}
    static var instance : Suit{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // an array with all of the raw values
    static var allValues: [String]{
        var values = [String]()

        let mirror = Mirror(reflecting: Suit.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? String else{continue}
            values.append(suit)
        }

        return values
    }
}

Если вы используете этот метод, чтобы получить одно значение, вам нужно использовать Suit.instance.clubs или Suit.instance.spades

Но все это так скучно... Позвольте сделать кое-что, что делает это больше как реальное перечисление!

public struct SuitType{

    // store multiple things for each suit
    let spades = Suit("", order: 4)
    let hearts = Suit("", order: 3)
    let diamonds = Suit("", order: 2)
    let clubs = Suit("", order: 1)

    struct SStruct{static var instance: SuitType = SuitType()}
    static var instance : SuitType{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // a dictionary mapping the raw values to the values
    static var allValuesDictionary: [String : Suit]{
        var values = [String : Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values[suit.rawValue] = suit
        }

        return values
    }
}

public struct Suit: RawRepresentable, Hashable{
    public var rawValue: String
    public typealias RawValue = String

    public var hashValue: Int{
        // find some integer that can be used to uniquely identify
        // each value. In this case, we could have used the order
        // variable because it is a unique value, yet to make this
        // apply to more cases, the hash table address of rawValue
        // will be returned, which should work in almost all cases
        // 
        // you could also add a hashValue parameter to init() and
        // give each suit a different hash value
        return rawValue.hash
    }

    public var order: Int
    public init(_ value: String, order: Int){
        self.rawValue = value
        self.order = order
    }

    // an array of all of the Suit values
    static var allValues: [Suit]{
        var values = [Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values.append(suit)
        }

        return values
    }

    // allows for using Suit(rawValue: ""), like a normal enum
    public init?(rawValue: String){
        // get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
        guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
        // initialize a new Suit with the same properties as that with the same raw value
        self.init(suit.rawValue, order: suit.order)
    }
}

Теперь вы можете делать что-то вроде

let allSuits: [Suit] = Suit.allValues

или

for suit in Suit.allValues{
   print("The suit \(suit.rawValue) has the order \(suit.order)")
}

Однако, чтобы получить сингл, вам все равно придется использовать SuitType.instance.spades или SuitType.instance.hearts. Чтобы сделать это более интуитивно понятным, вы можете добавить код Suit, который позволяет использовать Suit.type.* вместо SuitType.instance.*

public struct Suit: RawRepresentable, Hashable{
   // ...your code...

   static var type = SuitType.instance

   // ...more of your code...
}

Теперь вы можете использовать Suit.type.diamonds вместо SuitType.instance.diamonds или Suit.type.clubs вместо SuitType.instance.clubs

0

Я добавил функцию count() и повторил значения:

public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5

static func count() -> Int {
    return (ppg.rawValue) + 1
}

static var allValues: [MetricType] {
    var array: [MetricType] = Array()
    var item : MetricType = MetricType.mvps
    while item.rawValue < MetricType.count() {
        array.append(item)
        item = MetricType(rawValue: (item.rawValue + 1))!
    }
    return array
}

}

0

Адаптация @rintaro для Swift 3, в котором rintaro:

  • написал служебную функцию iterateEnum() для итерации для произвольных типов перечислений
  • реализованы идеи литья из @Kametrixom answer

Swift3

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

        let value : T?
        if next.hashValue == i {
            value = next
        } else {
            value = nil
        }
        i = i + 1
        return value
    }
}
0

Мое решение состоит в том, чтобы объявить массив со всеми возможностями перечисления, так что для него можно пройти через все.

//Function inside struct Card
static func generateFullDeck() -> [Card] {
    let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
    let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
    var myFullDeck: [Card] = []

    for myRank in allRanks {
        for mySuit in allSuits {
            myFullDeck.append(Card(rank: myRank, suit: mySuit))
        }
    }
    return myFullDeck
}

//actual use:
let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck

var allDesc: [String] = []
for aCard in aFullDeck {
    println(aCard.simpleDescription())    //You'll see all the results in playground
}
0

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

public class MyClassThatLoadsTexturesEtc
{
    //...

    // Colors used for gems and sectors.
    public enum Color: Int
    {
        // Colors arranged in order of the spectrum.
        case First = 0
        case Red, Orange, Yellow, Green, Blue, Purple, Pink
        // --> Add more colors here, between the first and last markers.
        case Last
    }

    //...

    public func preloadGems()
    {
        // Preload all gems.
        for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
        {
            let color = Color.fromRaw(i)!
            loadColoredTextures(forKey: color)
        }
    }

    //...
}
  • 0
    Вопрос специально для типа String , а не типа Int .
0

Мне потребовалось немного больше, чем один метод в структуре, как это требовала быстрая книга, но я создал следующие функции в перечислении. Я бы использовал протокол, я не уверен, почему, но имея ранг, установленный как int, беспорядок его

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seve, 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.toRaw())
        }
    }
    mutating func next() -> Rank {
        var rank = self
        var rawrank = rank.toRaw()
        var nrank:Rank = self
        rawrank = rawrank + 1
        if let newRank = Rank.fromRaw(rawrank) {
            println("\(newRank.simpleDescription())")
            nrank = newRank
        } else {
            return self
        }
        return nrank
    }
}

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func color() -> String {
        switch self{
        case .Spades, .Clubs:
            return "black"
        default:
            return "red"
        }
    }
    func simpleDescription() -> String {
        switch self{
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
    mutating func next() -> Suit {
        switch self{
        case .Spades:
            return Hearts
        case .Hearts:
            return Diamonds
        case .Diamonds:
            return Clubs
        case .Clubs:
            return Spades
        }
    }
}

struct Card {
    var rank:Rank
    var suit:Suit
    func deck() -> Card[] {
        var tRank = self.rank
        var tSuit = self.suit
        let tcards = 52 // we start from 0
        var cards: Card[] = []
        for i in 0..tcards{
            var card = Card(rank: tRank, suit: tSuit)
            cards.append(card)
            tRank = tRank.next()
            tSuit = tSuit.next()
        }
        return cards
    }
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}

var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

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

-1
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.toRaw())
        }
    }
}

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, .Clubs: return "black"
        case .Hearts, .Diamonds: return "red"
        }
    }
}

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    static func createPokers() -> Card[] {
        let ranks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
        let suits = Array(Suit.Spades.toRaw()...Suit.Clubs.toRaw())
        let cards = suits.reduce(Card[]()) { (tempCards, suit) in
            tempCards + ranks.map { rank in
                Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
            }
        }
        return cards
    }
}
-1

В Swift доступны типы enum, такие как EnumType.Case:

let tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)

В большинстве случаев вы будете использовать типы enum, если у вас есть несколько вариантов работы, и точно знать, что вы собираетесь делать на каждом из них.

Было бы нецелесообразно использовать структуру for-in при работе с типами enum.

Вы можете сделать это, например:

func sumNumbers(numbers : Int...) -> Int {
    var sum = 0

    for number in numbers{
        sum += number
    }

    return sum
}
  • 4
    Я не думаю, что ваше утверждение вообще верно. Весь смысл перечисления в том, что я хочу выразить диапазон значений (любого типа). Так же, как в спецификации цикла for я могу указать диапазон целых чисел, я могу захотеть сделать то же самое с более сложными типами. Я думаю, что Apple упустила уловку здесь.
  • 2
    Перечисления не для диапазонов, для выражения возможных состояний чего-либо. Они не являются коллекциями, они не выражают «диапазон», поэтому вы не можете просмотреть их, это просто не имеет смысла.
Показать ещё 11 комментариев
-2

Мой скромный вклад... Я играл и пришел к этому. Поверхность заключается в том, что она очень читаема для меня. Небольшой недостаток заключается в том, что для создания массива enumerate'able потребовалось некоторое дополнительное типирование.

enum Rank: Int {

case Ace
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King

case All

func simpleDescription() -> String {

    switch self {

    case .Ace:
        return "ace"
    case .Jack:
        return "jack"
    case .Queen:
        return "queen"
    case .King:
        return "king"
    case .All:
        return "all"
    default:
        return String(self.toRaw())

    }

}

func values() -> Array<Rank> {

    let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]

return allRanks

}}

enum Suit {

case Spades, Hearts, Diamonds, Clubs
case All

func simpleDescription() -> String {

    switch self {
    case .Spades:
        return "spades"
    case .Hearts:
        return "hearts"
    case .Diamonds:
        return "diamonds"
    case .Clubs:
        return "clubs"
    case .All:
        return "all"

    }
}

func values() -> Array<Suit> {

    let allSuits = [Suit.Spades, Suit.Hearts, Suit.Diamonds, Suit.Clubs]

return allSuits

}}

struct Card {

var rank: Rank
var suit: Suit

func simpleDescription() -> String {

    return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"

}}

var deck = Card[]()

for aSuit: Suit in Suit.All.values() {

    for aRank: Rank in Rank.All.values() {

       let card = Card(rank: aRank, suit: aSuit)

       deck += card

       println("\(card.simpleDescription())")

}}
-2

Здесь менее загадочный пример, если вы все еще хотели использовать перечисления для Rank И Suit. Просто соберите их в массив, если вы хотите использовать for-in loop для итерации по каждому из них.

Пример стандартной колоды 52-карточек:

enum Rank: Int {
    case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
    func name() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}

enum Suit: Int {
    case Diamonds = 1, Clubs, Hearts, Spades
    func name() -> String {
        switch self {
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        case .Hearts:
            return "hearts"
        case .Spades:
            return "spades"
        default:
            return "NOT A VALID SUIT"
        }
    }
}

let Ranks = [
    Rank.Ace,
    Rank.Two,
    Rank.Three,
    Rank.Four,
    Rank.Five,
    Rank.Six,
    Rank.Seven,
    Rank.Eight,
    Rank.Nine,
    Rank.Ten,
    Rank.Jack,
    Rank.Queen,
    Rank.King
]

let Suits = [
    Suit.Diamonds,
    Suit.Clubs,
    Suit.Hearts,
    Suit.Spades
]


class Card {
    var rank: Rank
    var suit: Suit

    init(rank: Rank, suit: Suit) {
        self.rank = rank
        self.suit = suit
    }
}

class Deck {
    var cards = Card[]()

    init() {
        for rank in Ranks {
            for suit in Suits {
                cards.append(Card(rank: rank, suit: suit))
            }
        }
    }
}

var myDeck = Deck()
myDeck.cards.count  // => 52
  • 5
    Это побеждает одну из главных целей наличия перечислений во-первых. То есть список определенных значений должен находиться в одном и только одном месте. Если используемое вами перечисление определено в отдельной библиотеке, и новая версия этой библиотеки добавляет значение, вам нужно будет помнить каждое место во всех ваших проектах, где вы использовали этот подход, изменять код и перекомпилировать , И давайте надеяться, что разработчик, который ушел два года назад, где-то записал это (он не сделал, или если он сделал, никто не помнит, где). Это был бы кошмар обслуживания.
  • 0
    Конечно, в ООП он больше похож на шаблон композиции, но это разумная реализация использования enum. Перечисление просто предоставляет допустимый набор «видов» «чего-то». Преимущество здесь в том, что существует фиксированный «перечислимый» набор этих «видов», который может быть использован чем угодно, не таща с собой полноценный класс Карты.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню