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

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

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

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

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

♠
♥
♦
♣

41 ответ

С некоторыми изменениями в ответе ( /questions/21807433/kak-perechislit-perechislenie-so-strokovyim-tipom/21807557#21807557) и поддержке Swift 4.

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

    static func iterateEnum() -> AnyIterator<Suit> {
        var i = 0
        return AnyIterator {
            let next = withUnsafeBytes(of: &i) { $0.load(as: Suit.self) }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
}

for f in Suit.iterateEnum() {
    print(f.rawValue)
}

Результат:




(Улучшение 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]
        }
    }
}

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

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

    //...
}

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

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

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

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

    return cards
}

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

Попробуй это:

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

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

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

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

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

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

Адаптируем ответ @rintaro к Swift 3, в котором rintaro:

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

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
    }
}
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 раз быстрее

Это заняло у меня немного больше, чем один метод в структуре, такой как быстрая книга, но я настроил следующие функции в enum. Я бы использовал протокол, я не уверен, почему, но с присвоенным ему рангом 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()

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

Я добавил функцию 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
}

}

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

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

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

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

На Свифте, 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
}
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
    }
}

Вот менее загадочный пример, если вы все еще хотите использовать перечисления для 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
Другие вопросы по тегам